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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1902 lines
54 KiB
C
Raw Permalink Normal View History

/*
* 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) 2007, Blender Foundation
* This is a new part of Blender
*/
2011-02-27 20:29:51 +00:00
/** \file
* \ingroup edarmature
2011-02-27 20:29:51 +00:00
*/
#include <math.h>
#include <string.h>
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_dlrbTree.h"
#include "BLI_string_utils.h"
#include "BLT_translation.h"
2011-06-15 11:41:15 +00:00
#include "DNA_anim_types.h"
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_armature.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "DEG_depsgraph.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "ED_anim_api.h"
#include "ED_armature.h"
#include "ED_keyframes_draw.h"
#include "ED_keyframes_edit.h"
#include "ED_keyframing.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "armature_intern.h"
/* ******* XXX ********** */
static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUSED(c))
{
}
/* ************************************************************* */
/**
* Pose-Library Tool for Blender
* =============================
*
* Overview:
2012-05-08 20:18:33 +00:00
* This tool allows animators to store a set of frequently used poses to dump into
* the active action to help in "budget" productions to quickly block out new actions.
* It acts as a kind of "glorified clipboard for poses", allowing for naming of poses.
*
* Features:
2020-07-07 12:44:47 +10:00
* - Pose-libs are simply normal Actions.
* - Each "pose" is simply a set of key-frames that occur on a particular frame.
* - A set of #TimeMarker that belong to each Action, help 'label' where a 'pose' can be
* found in the Action.
2020-07-07 12:44:47 +10:00
* - The Scroll-wheel or PageUp/Down buttons when used in a special mode or after pressing/holding
* [a modifier] key, cycles through the poses available for the active pose's pose-lib,
* allowing the animator to preview what action best suits that pose.
*/
/* ************************************************************* */
/* gets the first available frame in poselib to store a pose on
2018-11-14 12:53:15 +11:00
* - frames start from 1, and a pose should occur on every frame... 0 is error!
*/
2012-05-08 20:18:33 +00:00
static int poselib_get_free_index(bAction *act)
{
TimeMarker *marker;
2012-05-08 20:18:33 +00:00
int low = 0, high = 0;
bool changed = false;
/* sanity checks */
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, act, act->markers.first)) {
return 1;
2019-04-22 09:19:45 +10:00
}
/* As poses are not stored in chronological order, we must iterate over this list
* a few times until we don't make any new discoveries (mostly about the lower bound).
* Prevents problems with deleting then trying to add new poses T27412.
*/
do {
changed = false;
2012-05-08 20:18:33 +00:00
for (marker = act->markers.first; marker; marker = marker->next) {
/* only increase low if value is 1 greater than low, to find "gaps" where
* poses were removed from the poselib
*/
if (marker->frame == (low + 1)) {
low++;
changed = true;
}
/* value replaces high if it is the highest value encountered yet */
if (marker->frame > high) {
2012-05-08 20:18:33 +00:00
high = marker->frame;
changed = true;
}
}
} while (changed != 0);
/* - if low is not equal to high, then low+1 is a gap
* - if low is equal to high, then high+1 is the next index (add at end)
*/
2019-04-22 09:19:45 +10:00
if (low < high) {
return (low + 1);
2019-04-22 09:19:45 +10:00
}
return (high + 1);
}
/* returns the active pose for a poselib */
2012-05-08 20:18:33 +00:00
static TimeMarker *poselib_get_active_pose(bAction *act)
{
2019-04-22 09:19:45 +10:00
if ((act) && (act->active_marker)) {
2012-05-08 20:18:33 +00:00
return BLI_findlink(&act->markers, act->active_marker - 1);
2019-04-22 09:19:45 +10:00
}
return NULL;
}
/* Get object that Pose Lib should be found on */
2012-05-08 20:18:33 +00:00
/* XXX C can be zero */
static Object *get_poselib_object(bContext *C)
{
ScrArea *area;
/* sanity check */
2019-04-22 09:19:45 +10:00
if (C == NULL) {
return NULL;
2019-04-22 09:19:45 +10:00
}
area = CTX_wm_area(C);
if (area && (area->spacetype == SPACE_PROPERTIES)) {
return ED_object_context(C);
2019-04-22 09:19:45 +10:00
}
return BKE_object_pose_armature_get(CTX_data_active_object(C));
}
/* Poll callback for operators that require existing PoseLib data (with poses) to work */
2018-07-02 11:47:00 +02:00
static bool has_poselib_pose_data_poll(bContext *C)
{
Object *ob = get_poselib_object(C);
return (ob && ob->poselib);
}
/* Poll callback for operators that require existing PoseLib data (with poses)
* as they need to do some editing work on those poses (i.e. not on lib-linked actions)
*/
2018-07-02 11:47:00 +02:00
static bool has_poselib_pose_data_for_editing_poll(bContext *C)
{
Object *ob = get_poselib_object(C);
return (ob && ob->poselib && !ID_IS_LINKED(ob->poselib));
}
/* ----------------------------------- */
2012-03-01 12:20:18 +00:00
/* Initialize a new poselib (whether it is needed or not) */
static bAction *poselib_init_new(Main *bmain, Object *ob)
{
/* sanity checks - only for armatures */
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, ob, ob->pose)) {
return NULL;
2019-04-22 09:19:45 +10:00
}
/* init object's poselib action (unlink old one if there) */
2019-04-22 09:19:45 +10:00
if (ob->poselib) {
id_us_min(&ob->poselib->id);
2019-04-22 09:19:45 +10:00
}
ob->poselib = BKE_action_add(bmain, "PoseLib");
ob->poselib->idroot = ID_OB;
return ob->poselib;
}
2012-03-01 12:20:18 +00:00
/* Initialize a new poselib (checks if that needs to happen) */
static bAction *poselib_validate(Main *bmain, Object *ob)
{
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, ob, ob->pose)) {
return NULL;
2019-04-22 09:19:45 +10:00
}
if (ob->poselib == NULL) {
return poselib_init_new(bmain, ob);
2019-04-22 09:19:45 +10:00
}
return ob->poselib;
}
/* ************************************************************* */
/* Pose Lib UI Operators */
2012-05-08 20:18:33 +00:00
static int poselib_new_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
Object *ob = get_poselib_object(C);
/* sanity checks */
2019-04-22 09:19:45 +10:00
if (ob == NULL) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
/* new method here deals with the rest... */
poselib_init_new(bmain, ob);
/* notifier here might evolve? */
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
return OPERATOR_FINISHED;
}
void POSELIB_OT_new(wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Pose Library";
ot->idname = "POSELIB_OT_new";
ot->description = "Add New Pose Library to active Object";
/* callbacks */
ot->exec = poselib_new_exec;
ot->poll = ED_operator_posemode;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ------------------------------------------------ */
2012-05-08 20:18:33 +00:00
static int poselib_unlink_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = get_poselib_object(C);
/* sanity checks */
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, ob, ob->poselib)) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
/* there should be a poselib (we just checked above!), so just lower its user count and remove */
id_us_min(&ob->poselib->id);
ob->poselib = NULL;
/* notifier here might evolve? */
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL);
return OPERATOR_FINISHED;
}
void POSELIB_OT_unlink(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Unlink Pose Library";
ot->idname = "POSELIB_OT_unlink";
ot->description = "Remove Pose Library from active Object";
/* callbacks */
ot->exec = poselib_unlink_exec;
ot->poll = has_poselib_pose_data_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ************************************************************* */
/* Pose Editing Operators */
/* This tool automagically generates/validates poselib data so that it corresponds to the data
* in the action. This is for use in making existing actions usable as poselibs.
*/
static int poselib_sanitize_exec(bContext *C, wmOperator *op)
{
Object *ob = get_poselib_object(C);
2012-05-08 20:18:33 +00:00
bAction *act = (ob) ? ob->poselib : NULL;
DLRBT_Tree keys;
ActKeyColumn *ak;
TimeMarker *marker, *markern;
/* validate action */
2012-02-27 10:35:39 +00:00
if (act == NULL) {
BKE_report(op->reports, RPT_WARNING, "No action to validate");
return OPERATOR_CANCELLED;
}
/* determine which frames have keys */
BLI_dlrbTree_init(&keys);
action_to_keylist(NULL, act, &keys, 0);
/* for each key, make sure there is a corresponding pose */
2012-05-08 20:18:33 +00:00
for (ak = keys.first; ak; ak = ak->next) {
/* check if any pose matches this */
/* TODO: don't go looking through the list like this every time... */
2012-05-08 20:18:33 +00:00
for (marker = act->markers.first; marker; marker = marker->next) {
if (IS_EQ((double)marker->frame, (double)ak->cfra)) {
marker->flag = -1;
break;
}
}
/* add new if none found */
if (marker == NULL) {
/* add pose to poselib */
2012-05-08 20:18:33 +00:00
marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker");
BLI_snprintf(marker->name, sizeof(marker->name), "F%d Pose", (int)ak->cfra);
2012-05-08 20:18:33 +00:00
marker->frame = (int)ak->cfra;
marker->flag = -1;
BLI_addtail(&act->markers, marker);
}
}
/* remove all untagged poses (unused), and remove all tags */
2012-05-08 20:18:33 +00:00
for (marker = act->markers.first; marker; marker = markern) {
markern = marker->next;
2019-04-22 09:19:45 +10:00
if (marker->flag != -1) {
BLI_freelinkN(&act->markers, marker);
2019-04-22 09:19:45 +10:00
}
else {
marker->flag = 0;
2019-04-22 09:19:45 +10:00
}
}
/* free temp memory */
BLI_dlrbTree_free(&keys);
/* send notifiers for this - using keyframe editing notifiers, since action
* may be being shown in anim editors as active action
*/
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
void POSELIB_OT_action_sanitize(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Sanitize Pose Library Action";
ot->idname = "POSELIB_OT_action_sanitize";
ot->description = "Make action suitable for use as a Pose Library";
/* callbacks */
ot->exec = poselib_sanitize_exec;
ot->poll = has_poselib_pose_data_for_editing_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ------------------------------------------ */
/* Poll callback for adding poses to a PoseLib */
2018-07-02 11:47:00 +02:00
static bool poselib_add_poll(bContext *C)
{
/* There are 2 cases we need to be careful with:
* 1) When this operator is invoked from a hotkey, there may be no PoseLib yet
* 2) If a PoseLib already exists, we can't edit the action if it is a lib-linked
* actions, as data will be lost when saving the file
*/
if (ED_operator_posemode(C)) {
Object *ob = get_poselib_object(C);
if (ob) {
if ((ob->poselib == NULL) || !ID_IS_LINKED(ob->poselib)) {
return true;
}
}
}
return false;
}
2012-05-08 20:18:33 +00:00
static void poselib_add_menu_invoke__replacemenu(bContext *C, uiLayout *layout, void *UNUSED(arg))
{
2012-05-08 20:18:33 +00:00
Object *ob = get_poselib_object(C);
bAction *act = ob->poselib; /* never NULL */
TimeMarker *marker;
wmOperatorType *ot = WM_operatortype_find("POSELIB_OT_pose_add", 1);
BLI_assert(ot != NULL);
/* set the operator execution context correctly */
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
/* add each marker to this menu */
for (marker = act->markers.first; marker; marker = marker->next) {
PointerRNA props_ptr;
uiItemFullO_ptr(
layout, ot, marker->name, ICON_ARMATURE_DATA, NULL, WM_OP_EXEC_DEFAULT, 0, &props_ptr);
RNA_int_set(&props_ptr, "frame", marker->frame);
RNA_string_set(&props_ptr, "name", marker->name);
}
}
static int poselib_add_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
2012-05-08 20:18:33 +00:00
Scene *scene = CTX_data_scene(C);
Object *ob = get_poselib_object(C);
bPose *pose = (ob) ? ob->pose : NULL;
uiPopupMenu *pup;
uiLayout *layout;
/* sanity check */
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, ob, pose)) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
/* start building */
pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
layout = UI_popup_menu_layout(pup);
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
/* add new (adds to the first unoccupied frame) */
uiItemIntO(layout,
IFACE_("Add New"),
ICON_NONE,
"POSELIB_OT_pose_add",
"frame",
poselib_get_free_index(ob->poselib));
/* check if we have any choices to add a new pose in any other way */
if ((ob->poselib) && (ob->poselib->markers.first)) {
/* add new (on current frame) */
uiItemIntO(layout,
IFACE_("Add New (Current Frame)"),
ICON_NONE,
"POSELIB_OT_pose_add",
"frame",
CFRA);
/* replace existing - submenu */
uiItemMenuF(
layout, IFACE_("Replace Existing..."), 0, poselib_add_menu_invoke__replacemenu, NULL);
}
UI_popup_menu_end(C, pup);
/* this operator is only for a menu, not used further */
return OPERATOR_INTERFACE;
}
2012-05-08 20:18:33 +00:00
static int poselib_add_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
2012-05-08 20:18:33 +00:00
Object *ob = get_poselib_object(C);
bAction *act = poselib_validate(bmain, ob);
2012-05-08 20:18:33 +00:00
bPose *pose = (ob) ? ob->pose : NULL;
TimeMarker *marker;
KeyingSet *ks;
2012-05-08 20:18:33 +00:00
int frame = RNA_int_get(op->ptr, "frame");
char name[64];
/* sanity check (invoke should have checked this anyway) */
2019-04-22 09:19:45 +10:00
if (ELEM(NULL, ob, pose)) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
/* get name to give to pose */
RNA_string_get(op->ptr, "name", name);
/* add pose to poselib - replaces any existing pose there
2018-11-14 12:53:15 +11:00
* - for the 'replace' option, this should end up finding the appropriate marker,
* so no new one will be added
*/
2012-05-08 20:18:33 +00:00
for (marker = act->markers.first; marker; marker = marker->next) {
if (marker->frame == frame) {
BLI_strncpy(marker->name, name, sizeof(marker->name));
break;
}
}
if (marker == NULL) {
2012-05-08 20:18:33 +00:00
marker = MEM_callocN(sizeof(TimeMarker), "ActionMarker");
BLI_strncpy(marker->name, name, sizeof(marker->name));
2012-05-08 20:18:33 +00:00
marker->frame = frame;
BLI_addtail(&act->markers, marker);
}
/* validate name */
BLI_uniquename(
&act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name));
/* use Keying Set to determine what to store for the pose */
/* this includes custom props :)*/
ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_WHOLE_CHARACTER_SELECTED_ID);
ANIM_apply_keyingset(C, NULL, act, ks, MODIFYKEY_MODE_INSERT, (float)frame);
/* store new 'active' pose number */
act->active_marker = BLI_listbase_count(&act->markers);
DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
/* done */
return OPERATOR_FINISHED;
}
void POSELIB_OT_pose_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "PoseLib Add Pose";
ot->idname = "POSELIB_OT_pose_add";
ot->description = "Add the current Pose to the active Pose Library";
/* api callbacks */
ot->invoke = poselib_add_menu_invoke;
ot->exec = poselib_add_exec;
ot->poll = poselib_add_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_int(ot->srna, "frame", 1, 0, INT_MAX, "Frame", "Frame to store pose on", 0, INT_MAX);
RNA_def_string(ot->srna, "name", "Pose", 64, "Pose Name", "Name of newly added Pose");
}
/* ----- */
/* can be called with C == NULL */
static const EnumPropertyItem *poselib_stored_pose_itemf(bContext *C,
PointerRNA *UNUSED(ptr),
PropertyRNA *UNUSED(prop),
bool *r_free)
{
Object *ob = get_poselib_object(C);
bAction *act = (ob) ? ob->poselib : NULL;
TimeMarker *marker;
2012-05-08 20:18:33 +00:00
EnumPropertyItem *item = NULL, item_tmp = {0};
int totitem = 0;
int i = 0;
if (C == NULL) {
return DummyRNA_NULL_items;
}
/* check that the action exists */
if (act) {
/* add each marker to the list */
2012-05-08 20:18:33 +00:00
for (marker = act->markers.first, i = 0; marker; marker = marker->next, i++) {
item_tmp.identifier = item_tmp.name = marker->name;
item_tmp.icon = ICON_ARMATURE_DATA;
item_tmp.value = i;
RNA_enum_item_add(&item, &totitem, &item_tmp);
}
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
2012-05-08 20:18:33 +00:00
static int poselib_remove_exec(bContext *C, wmOperator *op)
{
2012-05-08 20:18:33 +00:00
Object *ob = get_poselib_object(C);
bAction *act = (ob) ? ob->poselib : NULL;
TimeMarker *marker;
int marker_index;
FCurve *fcu;
PropertyRNA *prop;
/* check if valid poselib */
if (act == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
return OPERATOR_CANCELLED;
}
prop = RNA_struct_find_property(op->ptr, "pose");
if (RNA_property_is_set(op->ptr, prop)) {
marker_index = RNA_property_enum_get(op->ptr, prop);
}
else {
marker_index = act->active_marker - 1;
}
/* get index (and pointer) of pose to remove */
marker = BLI_findlink(&act->markers, marker_index);
if (marker == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
return OPERATOR_CANCELLED;
}
/* remove relevant keyframes */
2012-05-08 20:18:33 +00:00
for (fcu = act->curves.first; fcu; fcu = fcu->next) {
BezTriple *bezt;
uint i;
if (fcu->bezt) {
2012-05-08 20:18:33 +00:00
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
/* check if remove */
if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) {
delete_fcurve_key(fcu, i, 1);
break;
}
}
}
}
/* remove poselib from list */
BLI_freelinkN(&act->markers, marker);
/* fix active pose number */
2012-05-08 20:18:33 +00:00
act->active_marker = 0;
/* send notifiers for this - using keyframe editing notifiers, since action
* may be being shown in anim editors as active action
*/
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
DEG_id_tag_update(&act->id, ID_RECALC_COPY_ON_WRITE);
/* done */
return OPERATOR_FINISHED;
}
void POSELIB_OT_pose_remove(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "PoseLib Remove Pose";
ot->idname = "POSELIB_OT_pose_remove";
ot->description = "Remove nth pose from the active Pose Library";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = poselib_remove_exec;
ot->poll = has_poselib_pose_data_for_editing_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to remove");
RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
}
static int poselib_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
2012-05-08 20:18:33 +00:00
Object *ob = get_poselib_object(C);
bAction *act = (ob) ? ob->poselib : NULL;
TimeMarker *marker;
/* check if valid poselib */
if (act == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
return OPERATOR_CANCELLED;
}
/* get index (and pointer) of pose to remove */
2012-05-08 20:18:33 +00:00
marker = BLI_findlink(&act->markers, act->active_marker - 1);
if (marker == NULL) {
BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
return OPERATOR_CANCELLED;
}
/* Use the existing name of the marker as the name,
* and use the active marker as the one to rename. */
RNA_enum_set(op->ptr, "pose", act->active_marker - 1);
RNA_string_set(op->ptr, "name", marker->name);
/* part to sync with other similar operators... */
return WM_operator_props_popup_confirm(C, op, event);
}
2012-05-08 20:18:33 +00:00
static int poselib_rename_exec(bContext *C, wmOperator *op)
{
2012-05-08 20:18:33 +00:00
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
bAction *act = (ob) ? ob->poselib : NULL;
TimeMarker *marker;
char newname[64];
/* check if valid poselib */
if (act == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
return OPERATOR_CANCELLED;
}
/* get index (and pointer) of pose to remove */
marker = BLI_findlink(&act->markers, RNA_enum_get(op->ptr, "pose"));
if (marker == NULL) {
BKE_report(op->reports, RPT_ERROR, "Invalid index for pose");
return OPERATOR_CANCELLED;
}
/* get new name */
RNA_string_get(op->ptr, "name", newname);
/* copy name and validate it */
BLI_strncpy(marker->name, newname, sizeof(marker->name));
BLI_uniquename(
&act->markers, marker, DATA_("Pose"), '.', offsetof(TimeMarker, name), sizeof(marker->name));
/* send notifiers for this - using keyframe editing notifiers, since action
* may be being shown in anim editors as active action
*/
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
/* done */
return OPERATOR_FINISHED;
}
void POSELIB_OT_pose_rename(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "PoseLib Rename Pose";
ot->idname = "POSELIB_OT_pose_rename";
ot->description = "Rename specified pose from the active Pose Library";
/* api callbacks */
ot->invoke = poselib_rename_invoke;
ot->exec = poselib_rename_exec;
ot->poll = has_poselib_pose_data_for_editing_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
/* NOTE: name not pose is the operator's "main" property,
* so that it will get activated in the popup for easy renaming */
ot->prop = RNA_def_string(
ot->srna, "name", "RenamedPose", 64, "New Pose Name", "New name for pose");
prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to rename");
2012-05-08 20:18:33 +00:00
RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static int poselib_move_exec(bContext *C, wmOperator *op)
{
Object *ob = get_poselib_object(C);
bAction *act = (ob) ? ob->poselib : NULL;
TimeMarker *marker;
int marker_index;
int dir;
PropertyRNA *prop;
/* check if valid poselib */
if (act == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data");
return OPERATOR_CANCELLED;
}
prop = RNA_struct_find_property(op->ptr, "pose");
if (RNA_property_is_set(op->ptr, prop)) {
marker_index = RNA_property_enum_get(op->ptr, prop);
}
else {
marker_index = act->active_marker - 1;
}
/* get index (and pointer) of pose to remove */
marker = BLI_findlink(&act->markers, marker_index);
if (marker == NULL) {
BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index);
return OPERATOR_CANCELLED;
}
dir = RNA_enum_get(op->ptr, "direction");
/* move pose */
if (BLI_listbase_link_move(&act->markers, marker, dir)) {
act->active_marker = marker_index + dir + 1;
/* send notifiers for this - using keyframe editing notifiers, since action
* may be being shown in anim editors as active action
*/
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
else {
return OPERATOR_CANCELLED;
}
/* done */
return OPERATOR_FINISHED;
}
void POSELIB_OT_pose_move(wmOperatorType *ot)
{
PropertyRNA *prop;
static const EnumPropertyItem pose_lib_pose_move[] = {
{-1, "UP", 0, "Up", ""},
{1, "DOWN", 0, "Down", ""},
{0, NULL, 0, NULL, NULL},
};
/* identifiers */
ot->name = "PoseLib Move Pose";
ot->idname = "POSELIB_OT_pose_move";
ot->description = "Move the pose up or down in the active Pose Library";
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = poselib_move_exec;
ot->poll = has_poselib_pose_data_for_editing_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna, "pose", DummyRNA_NULL_items, 0, "Pose", "The pose to move");
RNA_def_enum_funcs(prop, poselib_stored_pose_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
RNA_def_enum(ot->srna,
"direction",
pose_lib_pose_move,
0,
"Direction",
"Direction to move the chosen pose towards");
}
/* ************************************************************* */
/* Pose-Lib Browsing/Previewing Operator */
/* Simple struct for storing settings/data for use during PoseLib preview */
typedef struct tPoseLib_PreviewData {
/** tPoseLib_Backup structs for restoring poses. */
ListBase backups;
/** LinkData structs storing list of poses which match the current search-string. */
ListBase searchp;
/** active scene. */
Scene *scene;
/** active area. */
ScrArea *area;
/** RNA-Pointer to Object 'ob' .*/
PointerRNA rna_ptr;
/** object to work on. */
Object *ob;
/** object's armature data. */
bArmature *arm;
/** object's pose. */
bPose *pose;
/** poselib to use. */
bAction *act;
/** 'active' pose. */
TimeMarker *marker;
/** total number of elements to work on. */
int totcount;
/** state of main loop. */
short state;
/** redraw/update settings during main loop. */
short redraw;
/** flags for various settings. */
short flag;
/** position of cursor in searchstr (cursor occurs before the item at the nominated index) */
short search_cursor;
/** (Part of) Name to search for to filter poses that get shown. */
char searchstr[64];
/** Previously set searchstr (from last loop run),
* so that we can detected when to rebuild searchp. */
char searchold[64];
/** Info-text to print in header. */
char headerstr[UI_MAX_DRAW_STR];
} tPoseLib_PreviewData;
/* defines for tPoseLib_PreviewData->state values */
enum {
PL_PREVIEW_ERROR = -1,
PL_PREVIEW_RUNNING,
PL_PREVIEW_CONFIRM,
PL_PREVIEW_CANCEL,
PL_PREVIEW_RUNONCE,
};
/* defines for tPoseLib_PreviewData->redraw values */
enum {
PL_PREVIEW_NOREDRAW = 0,
PL_PREVIEW_REDRAWALL,
PL_PREVIEW_REDRAWHEADER,
};
/* defines for tPoseLib_PreviewData->flag values */
enum {
2012-05-08 20:18:33 +00:00
PL_PREVIEW_FIRSTTIME = (1 << 0),
PL_PREVIEW_SHOWORIGINAL = (1 << 1),
PL_PREVIEW_ANY_BONE_SELECTED = (1 << 2),
};
/* ---------------------------- */
/* simple struct for storing backup info */
typedef struct tPoseLib_Backup {
struct tPoseLib_Backup *next, *prev;
2012-05-08 20:18:33 +00:00
bPoseChannel *pchan; /* pose channel backups are for */
2012-05-08 20:18:33 +00:00
bPoseChannel olddata; /* copy of pose channel's old data (at start) */
IDProperty *oldprops; /* copy (needs freeing) of pose channel's properties (at start) */
} tPoseLib_Backup;
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
2012-05-08 20:18:33 +00:00
static void poselib_backup_posecopy(tPoseLib_PreviewData *pld)
{
bActionGroup *agrp;
bPoseChannel *pchan;
bool selected = false;
/* determine whether any bone is selected. */
LISTBASE_FOREACH (bPoseChannel *, bchan, &pld->pose->chanbase) {
selected = bchan->bone != NULL && bchan->bone->flag & BONE_SELECTED;
if (selected) {
pld->flag |= PL_PREVIEW_ANY_BONE_SELECTED;
break;
}
}
if (!selected) {
pld->flag &= ~PL_PREVIEW_ANY_BONE_SELECTED;
}
/* for each posechannel that has an actionchannel in */
2012-05-08 20:18:33 +00:00
for (agrp = pld->act->groups.first; agrp; agrp = agrp->next) {
/* try to find posechannel */
2012-05-08 20:18:33 +00:00
pchan = BKE_pose_channel_find_name(pld->pose, agrp->name);
/* backup data if available */
if (pchan) {
tPoseLib_Backup *plb;
/* store backup */
2012-05-08 20:18:33 +00:00
plb = MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup");
2012-05-08 20:18:33 +00:00
plb->pchan = pchan;
memcpy(&plb->olddata, plb->pchan, sizeof(bPoseChannel));
2019-04-22 09:19:45 +10:00
if (pchan->prop) {
2012-05-08 20:18:33 +00:00
plb->oldprops = IDP_CopyProperty(pchan->prop);
2019-04-22 09:19:45 +10:00
}
BLI_addtail(&pld->backups, plb);
/* mark as being affected */
pld->totcount++;
}
}
}
/* Restores original pose */
2012-05-08 20:18:33 +00:00
static void poselib_backup_restore(tPoseLib_PreviewData *pld)
{
tPoseLib_Backup *plb;
2012-05-08 20:18:33 +00:00
for (plb = pld->backups.first; plb; plb = plb->next) {
/* copy most of data straight back */
memcpy(plb->pchan, &plb->olddata, sizeof(bPoseChannel));
/* just overwrite values of properties from the stored copies (there should be some) */
2019-04-22 09:19:45 +10:00
if (plb->oldprops) {
IDP_SyncGroupValues(plb->pchan->prop, plb->oldprops);
2019-04-22 09:19:45 +10:00
}
/* TODO: constraints settings aren't restored yet,
* even though these could change (though not that likely) */
}
}
/* Free list of backups, including any side data it may use */
2012-05-08 20:18:33 +00:00
static void poselib_backup_free_data(tPoseLib_PreviewData *pld)
{
tPoseLib_Backup *plb, *plbn;
2012-05-08 20:18:33 +00:00
for (plb = pld->backups.first; plb; plb = plbn) {
plbn = plb->next;
/* free custom data */
if (plb->oldprops) {
IDP_FreeProperty(plb->oldprops);
}
/* free backup element now */
BLI_freelinkN(&pld->backups, plb);
}
}
/* ---------------------------- */
/* Applies the appropriate stored pose from the pose-library to the current pose
2018-11-14 12:53:15 +11:00
* - assumes that a valid object, with a poselib has been supplied
* - gets the string to print in the header
* - this code is based on the code for extract_pose_from_action in blenkernel/action.c
*/
T77086 Animation: Passing Dependency Graph to Drivers Custom driver functions need access to the dependency graph that is triggering the evaluation of the driver. This patch passes the dependency graph pointer through all the animation-related calls. Instead of passing the evaluation time to functions, the code now passes an `AnimationEvalContext` pointer: ``` typedef struct AnimationEvalContext { struct Depsgraph *const depsgraph; const float eval_time; } AnimationEvalContext; ``` These structs are read-only, meaning that the code cannot change the evaluation time. Note that the `depsgraph` pointer itself is const, but it points to a non-const depsgraph. FCurves and Drivers can be evaluated at a different time than the current scene time, for example when evaluating NLA strips. This means that, even though the current time is stored in the dependency graph, we need an explicit evaluation time. There are two functions that allow creation of `AnimationEvalContext` objects: - `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float eval_time)`, which creates a new context object from scratch, and - `BKE_animsys_eval_context_construct_at(AnimationEvalContext *anim_eval_context, float eval_time)`, which can be used to create a `AnimationEvalContext` with the same depsgraph, but at a different time. This makes it possible to later add fields without changing any of the code that just want to change the eval time. This also provides a fix for T75553, although it does require a change to the custom driver function. The driver should call `custom_function(depsgraph)`, and the function should use that depsgraph instead of information from `bpy.context`. Reviewed By: brecht, sergey Differential Revision: https://developer.blender.org/D8047
2020-07-17 17:38:09 +02:00
static void poselib_apply_pose(tPoseLib_PreviewData *pld,
const AnimationEvalContext *anim_eval_context)
{
2012-05-08 20:18:33 +00:00
PointerRNA *ptr = &pld->rna_ptr;
bArmature *arm = pld->arm;
bPose *pose = pld->pose;
bPoseChannel *pchan;
2012-05-08 20:18:33 +00:00
bAction *act = pld->act;
bActionGroup *agrp;
2012-05-08 20:18:33 +00:00
KeyframeEditData ked = {{NULL}};
KeyframeEditFunc group_ok_cb;
2012-05-08 20:18:33 +00:00
int frame = 1;
const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED;
/* get the frame */
2019-04-22 09:19:45 +10:00
if (pld->marker) {
2012-05-08 20:18:33 +00:00
frame = pld->marker->frame;
2019-04-22 09:19:45 +10:00
}
else {
return;
2019-04-22 09:19:45 +10:00
}
/* init settings for testing groups for keyframes */
2012-05-08 20:18:33 +00:00
group_ok_cb = ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
ked.f1 = ((float)frame) - 0.5f;
ked.f2 = ((float)frame) + 0.5f;
T77086 Animation: Passing Dependency Graph to Drivers Custom driver functions need access to the dependency graph that is triggering the evaluation of the driver. This patch passes the dependency graph pointer through all the animation-related calls. Instead of passing the evaluation time to functions, the code now passes an `AnimationEvalContext` pointer: ``` typedef struct AnimationEvalContext { struct Depsgraph *const depsgraph; const float eval_time; } AnimationEvalContext; ``` These structs are read-only, meaning that the code cannot change the evaluation time. Note that the `depsgraph` pointer itself is const, but it points to a non-const depsgraph. FCurves and Drivers can be evaluated at a different time than the current scene time, for example when evaluating NLA strips. This means that, even though the current time is stored in the dependency graph, we need an explicit evaluation time. There are two functions that allow creation of `AnimationEvalContext` objects: - `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float eval_time)`, which creates a new context object from scratch, and - `BKE_animsys_eval_context_construct_at(AnimationEvalContext *anim_eval_context, float eval_time)`, which can be used to create a `AnimationEvalContext` with the same depsgraph, but at a different time. This makes it possible to later add fields without changing any of the code that just want to change the eval time. This also provides a fix for T75553, although it does require a change to the custom driver function. The driver should call `custom_function(depsgraph)`, and the function should use that depsgraph instead of information from `bpy.context`. Reviewed By: brecht, sergey Differential Revision: https://developer.blender.org/D8047
2020-07-17 17:38:09 +02:00
AnimationEvalContext anim_context_at_frame = BKE_animsys_eval_context_construct_at(
anim_eval_context, frame);
/* start applying - only those channels which have a key at this point in time! */
2012-05-08 20:18:33 +00:00
for (agrp = act->groups.first; agrp; agrp = agrp->next) {
/* check if group has any keyframes */
if (ANIM_animchanneldata_keyframes_loop(
&ked, NULL, agrp, ALE_GROUP, NULL, group_ok_cb, NULL)) {
/* has keyframe on this frame, so try to get a PoseChannel with this name */
2012-05-08 20:18:33 +00:00
pchan = BKE_pose_channel_find_name(pose, agrp->name);
if (pchan) {
2014-04-11 11:25:41 +10:00
bool ok = 0;
/* check if this bone should get any animation applied */
if (!any_bone_selected) {
/* if no bones are selected, then any bone is ok */
2012-05-08 20:18:33 +00:00
ok = 1;
}
else if (pchan->bone) {
/* only ok if bone is visible and selected */
if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->flag & BONE_HIDDEN_P) == 0 &&
(pchan->bone->layer & arm->layer)) {
ok = 1;
}
}
2019-04-22 09:19:45 +10:00
if (ok) {
T77086 Animation: Passing Dependency Graph to Drivers Custom driver functions need access to the dependency graph that is triggering the evaluation of the driver. This patch passes the dependency graph pointer through all the animation-related calls. Instead of passing the evaluation time to functions, the code now passes an `AnimationEvalContext` pointer: ``` typedef struct AnimationEvalContext { struct Depsgraph *const depsgraph; const float eval_time; } AnimationEvalContext; ``` These structs are read-only, meaning that the code cannot change the evaluation time. Note that the `depsgraph` pointer itself is const, but it points to a non-const depsgraph. FCurves and Drivers can be evaluated at a different time than the current scene time, for example when evaluating NLA strips. This means that, even though the current time is stored in the dependency graph, we need an explicit evaluation time. There are two functions that allow creation of `AnimationEvalContext` objects: - `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float eval_time)`, which creates a new context object from scratch, and - `BKE_animsys_eval_context_construct_at(AnimationEvalContext *anim_eval_context, float eval_time)`, which can be used to create a `AnimationEvalContext` with the same depsgraph, but at a different time. This makes it possible to later add fields without changing any of the code that just want to change the eval time. This also provides a fix for T75553, although it does require a change to the custom driver function. The driver should call `custom_function(depsgraph)`, and the function should use that depsgraph instead of information from `bpy.context`. Reviewed By: brecht, sergey Differential Revision: https://developer.blender.org/D8047
2020-07-17 17:38:09 +02:00
animsys_evaluate_action_group(ptr, act, agrp, &anim_context_at_frame);
2019-04-22 09:19:45 +10:00
}
}
}
}
}
/* Auto-keys/tags bones affected by the pose used from the poselib */
2012-05-08 20:18:33 +00:00
static void poselib_keytag_pose(bContext *C, Scene *scene, tPoseLib_PreviewData *pld)
{
2012-05-08 20:18:33 +00:00
bPose *pose = pld->pose;
bPoseChannel *pchan;
2012-05-08 20:18:33 +00:00
bAction *act = pld->act;
bActionGroup *agrp;
KeyingSet *ks = ANIM_get_keyingset_for_autokeying(scene, ANIM_KS_WHOLE_CHARACTER_ID);
ListBase dsources = {NULL, NULL};
2014-11-20 20:23:13 +01:00
bool autokey = autokeyframe_cfra_can_key(scene, &pld->ob->id);
const bool any_bone_selected = pld->flag & PL_PREVIEW_ANY_BONE_SELECTED;
/* start tagging/keying */
2012-05-08 20:18:33 +00:00
for (agrp = act->groups.first; agrp; agrp = agrp->next) {
/* only for selected bones unless there aren't any selected, in which case all are included */
2012-05-08 20:18:33 +00:00
pchan = BKE_pose_channel_find_name(pose, agrp->name);
if (pchan) {
if (!any_bone_selected || ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))) {
if (autokey) {
2021-02-05 16:23:34 +11:00
/* Add data-source override for the PoseChannel, to be used later. */
ANIM_relative_keyingset_add_source(&dsources, &pld->ob->id, &RNA_PoseBone, pchan);
}
}
}
}
/* perform actual auto-keying now */
if (autokey) {
/* insert keyframes for all relevant bones in one go */
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
BLI_freelistN(&dsources);
}
/* send notifiers for this */
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL);
}
/* Apply the relevant changes to the pose */
2012-05-08 20:18:33 +00:00
static void poselib_preview_apply(bContext *C, wmOperator *op)
{
2012-05-08 20:18:33 +00:00
tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata;
/* only recalc pose (and its dependencies) if pose has changed */
if (pld->redraw == PL_PREVIEW_REDRAWALL) {
2021-02-05 16:23:34 +11:00
/* Don't clear pose if first time. */
2019-04-22 09:19:45 +10:00
if ((pld->flag & PL_PREVIEW_FIRSTTIME) == 0) {
poselib_backup_restore(pld);
2019-04-22 09:19:45 +10:00
}
else {
pld->flag &= ~PL_PREVIEW_FIRSTTIME;
2019-04-22 09:19:45 +10:00
}
/* pose should be the right one to draw (unless we're temporarily not showing it) */
2012-05-08 20:18:33 +00:00
if ((pld->flag & PL_PREVIEW_SHOWORIGINAL) == 0) {
RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker));
T77086 Animation: Passing Dependency Graph to Drivers Custom driver functions need access to the dependency graph that is triggering the evaluation of the driver. This patch passes the dependency graph pointer through all the animation-related calls. Instead of passing the evaluation time to functions, the code now passes an `AnimationEvalContext` pointer: ``` typedef struct AnimationEvalContext { struct Depsgraph *const depsgraph; const float eval_time; } AnimationEvalContext; ``` These structs are read-only, meaning that the code cannot change the evaluation time. Note that the `depsgraph` pointer itself is const, but it points to a non-const depsgraph. FCurves and Drivers can be evaluated at a different time than the current scene time, for example when evaluating NLA strips. This means that, even though the current time is stored in the dependency graph, we need an explicit evaluation time. There are two functions that allow creation of `AnimationEvalContext` objects: - `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float eval_time)`, which creates a new context object from scratch, and - `BKE_animsys_eval_context_construct_at(AnimationEvalContext *anim_eval_context, float eval_time)`, which can be used to create a `AnimationEvalContext` with the same depsgraph, but at a different time. This makes it possible to later add fields without changing any of the code that just want to change the eval time. This also provides a fix for T75553, although it does require a change to the custom driver function. The driver should call `custom_function(depsgraph)`, and the function should use that depsgraph instead of information from `bpy.context`. Reviewed By: brecht, sergey Differential Revision: https://developer.blender.org/D8047
2020-07-17 17:38:09 +02:00
struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(
depsgraph, 0.0f /* poselib_apply_pose() determines its own evaluation time. */);
poselib_apply_pose(pld, &anim_eval_context);
}
2019-04-22 09:19:45 +10:00
else {
2012-05-08 20:18:33 +00:00
RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */
2019-04-22 09:19:45 +10:00
}
DEG_id_tag_update(&pld->ob->id, ID_RECALC_GEOMETRY);
}
/* do header print - if interactively previewing */
if (pld->state == PL_PREVIEW_RUNNING) {
if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
ED_area_status_text(pld->area, TIP_("PoseLib Previewing Pose: [Showing Original Pose]"));
ED_workspace_status_text(C, TIP_("Use Tab to start previewing poses again"));
}
else if (pld->searchstr[0]) {
char tempstr[65];
char markern[64];
short index;
/* get search-string */
2012-05-08 20:18:33 +00:00
index = pld->search_cursor;
if (index >= 0 && index < sizeof(tempstr) - 1) {
memcpy(&tempstr[0], &pld->searchstr[0], index);
2012-05-08 20:18:33 +00:00
tempstr[index] = '|';
memcpy(&tempstr[index + 1], &pld->searchstr[index], (sizeof(tempstr) - 1) - index);
}
else {
BLI_strncpy(tempstr, pld->searchstr, sizeof(tempstr));
}
/* get marker name */
BLI_strncpy(markern, pld->marker ? pld->marker->name : "No Matches", sizeof(markern));
BLI_snprintf(pld->headerstr,
sizeof(pld->headerstr),
TIP_("PoseLib Previewing Pose: Filter - [%s] | "
"Current Pose - \"%s\""),
tempstr,
markern);
ED_area_status_text(pld->area, pld->headerstr);
ED_workspace_status_text(C, TIP_("Use ScrollWheel or PageUp/Down to change pose"));
}
else {
BLI_snprintf(pld->headerstr,
sizeof(pld->headerstr),
TIP_("PoseLib Previewing Pose: \"%s\""),
pld->marker->name);
ED_area_status_text(pld->area, pld->headerstr);
ED_workspace_status_text(C, NULL);
}
}
/* request drawing of view + clear redraw flag */
2012-05-08 20:18:33 +00:00
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob);
pld->redraw = PL_PREVIEW_NOREDRAW;
}
/* ---------------------------- */
/* This helper function is called during poselib_preview_poses to find the
* pose to preview next (after a change event)
*/
2012-05-08 20:18:33 +00:00
static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step)
{
/* stop if not going anywhere, as we assume that there is a direction to move in */
2019-04-22 09:19:45 +10:00
if (step == 0) {
return;
2019-04-22 09:19:45 +10:00
}
/* search-string dictates a special approach */
if (pld->searchstr[0]) {
TimeMarker *marker;
LinkData *ld, *ldn, *ldc;
/* free and rebuild if needed (i.e. if search-str changed) */
if (!STREQ(pld->searchstr, pld->searchold)) {
/* free list of temporary search matches */
BLI_freelistN(&pld->searchp);
/* generate a new list of search matches */
2012-05-08 20:18:33 +00:00
for (marker = pld->act->markers.first; marker; marker = marker->next) {
/* does the name partially match?
2018-11-14 12:53:15 +11:00
* - don't worry about case, to make it easier for users to quickly input a name (or
* part of one), which is the whole point of this feature
*/
if (BLI_strcasestr(marker->name, pld->searchstr)) {
/* make link-data to store reference to it */
2012-05-08 20:18:33 +00:00
ld = MEM_callocN(sizeof(LinkData), "PoseMatch");
ld->data = marker;
BLI_addtail(&pld->searchp, ld);
}
}
/* set current marker to NULL (so that we start from first) */
2012-05-08 20:18:33 +00:00
pld->marker = NULL;
}
/* check if any matches */
if (BLI_listbase_is_empty(&pld->searchp)) {
2012-05-08 20:18:33 +00:00
pld->marker = NULL;
return;
}
/* find first match */
2012-05-08 20:18:33 +00:00
for (ldc = pld->searchp.first; ldc; ldc = ldc->next) {
2019-04-22 09:19:45 +10:00
if (ldc->data == pld->marker) {
break;
2019-04-22 09:19:45 +10:00
}
}
2019-04-22 09:19:45 +10:00
if (ldc == NULL) {
2012-05-08 20:18:33 +00:00
ldc = pld->searchp.first;
2019-04-22 09:19:45 +10:00
}
/* Loop through the matches in a cyclic fashion, incrementing/decrementing step as appropriate
* until step == 0. At this point, marker should be the correct marker.
*/
if (step > 0) {
2019-04-22 09:19:45 +10:00
for (ld = ldc; ld && step; ld = ldn, step--) {
2012-05-08 20:18:33 +00:00
ldn = (ld->next) ? ld->next : pld->searchp.first;
2019-04-22 09:19:45 +10:00
}
}
else {
2019-04-22 09:19:45 +10:00
for (ld = ldc; ld && step; ld = ldn, step++) {
2012-05-08 20:18:33 +00:00
ldn = (ld->prev) ? ld->prev : pld->searchp.last;
2019-04-22 09:19:45 +10:00
}
}
/* set marker */
2019-04-22 09:19:45 +10:00
if (ld) {
2012-05-08 20:18:33 +00:00
pld->marker = ld->data;
2019-04-22 09:19:45 +10:00
}
}
else {
TimeMarker *marker, *next;
/* if no marker, because we just ended searching, then set that to the start of the list */
2019-04-22 09:19:45 +10:00
if (pld->marker == NULL) {
2012-05-08 20:18:33 +00:00
pld->marker = pld->act->markers.first;
2019-04-22 09:19:45 +10:00
}
/* Loop through the markers in a cyclic fashion, incrementing/decrementing step as appropriate
* until step == 0. At this point, marker should be the correct marker.
*/
if (step > 0) {
2019-04-22 09:19:45 +10:00
for (marker = pld->marker; marker && step; marker = next, step--) {
2012-05-08 20:18:33 +00:00
next = (marker->next) ? marker->next : pld->act->markers.first;
2019-04-22 09:19:45 +10:00
}
}
else {
2019-04-22 09:19:45 +10:00
for (marker = pld->marker; marker && step; marker = next, step++) {
2012-05-08 20:18:33 +00:00
next = (marker->prev) ? marker->prev : pld->act->markers.last;
2019-04-22 09:19:45 +10:00
}
}
/* it should be fairly impossible for marker to be NULL */
2019-04-22 09:19:45 +10:00
if (marker) {
2012-05-08 20:18:33 +00:00
pld->marker = marker;
2019-04-22 09:19:45 +10:00
}
}
}
/* specially handle events for searching */
static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, ushort event_type, char ascii)
{
/* try doing some form of string manipulation first */
switch (event_type) {
case EVT_BACKSPACEKEY:
if (pld->searchstr[0] && pld->search_cursor) {
2012-05-08 20:18:33 +00:00
short len = strlen(pld->searchstr);
short index = pld->search_cursor;
short i;
2019-04-22 09:19:45 +10:00
for (i = index; i <= len; i++) {
2012-05-08 20:18:33 +00:00
pld->searchstr[i - 1] = pld->searchstr[i];
2019-04-22 09:19:45 +10:00
}
pld->search_cursor--;
poselib_preview_get_next(pld, 1);
pld->redraw = PL_PREVIEW_REDRAWALL;
return;
}
break;
case EVT_DELKEY:
if (pld->searchstr[0] && pld->searchstr[1]) {
2012-05-08 20:18:33 +00:00
short len = strlen(pld->searchstr);
short index = pld->search_cursor;
int i;
if (index < len) {
2019-04-22 09:19:45 +10:00
for (i = index; i < len; i++) {
2012-05-08 20:18:33 +00:00
pld->searchstr[i] = pld->searchstr[i + 1];
2019-04-22 09:19:45 +10:00
}
poselib_preview_get_next(pld, 1);
pld->redraw = PL_PREVIEW_REDRAWALL;
return;
}
}
break;
}
if (ascii) {
/* character to add to the string */
2012-05-08 20:18:33 +00:00
short index = pld->search_cursor;
short len = (pld->searchstr[0]) ? strlen(pld->searchstr) : 0;
short i;
if (len) {
2019-04-22 09:19:45 +10:00
for (i = len; i > index; i--) {
2012-05-08 20:18:33 +00:00
pld->searchstr[i] = pld->searchstr[i - 1];
2019-04-22 09:19:45 +10:00
}
}
2019-04-22 09:19:45 +10:00
else {
2012-05-08 20:18:33 +00:00
pld->searchstr[1] = 0;
2019-04-22 09:19:45 +10:00
}
2012-05-08 20:18:33 +00:00
pld->searchstr[index] = ascii;
pld->search_cursor++;
poselib_preview_get_next(pld, 1);
pld->redraw = PL_PREVIEW_REDRAWALL;
}
}
/* handle events for poselib_preview_poses */
static int poselib_preview_handle_event(bContext *UNUSED(C), wmOperator *op, const wmEvent *event)
{
2012-05-08 20:18:33 +00:00
tPoseLib_PreviewData *pld = op->customdata;
int ret = OPERATOR_RUNNING_MODAL;
/* only accept 'press' event, and ignore 'release', so that we don't get double actions */
if (ELEM(event->val, KM_PRESS, KM_NOTHING) == 0) {
#if 0
printf("PoseLib: skipping event with type '%s' and val %d\n",
WM_key_event_string(event->type, false),
event->val);
#endif
return ret;
}
/* backup stuff that needs to occur before every operation
2018-11-14 12:53:15 +11:00
* - make a copy of searchstr, so that we know if cache needs to be rebuilt
*/
BLI_strncpy(pld->searchold, pld->searchstr, sizeof(pld->searchold));
/* if we're currently showing the original pose, only certain events are handled */
if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
switch (event->type) {
/* exit - cancel */
case EVT_ESCKEY:
case RIGHTMOUSE:
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_CANCEL;
break;
/* exit - confirm */
case LEFTMOUSE:
case EVT_RETKEY:
case EVT_PADENTER:
case EVT_SPACEKEY:
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_CONFIRM;
break;
/* view manipulation */
/* we add pass through here, so that the operators responsible for these can still run,
* even though we still maintain control (as RUNNING_MODAL flag is still set too)
*/
case EVT_PAD0:
case EVT_PAD1:
case EVT_PAD2:
case EVT_PAD3:
case EVT_PAD4:
case EVT_PAD5:
case EVT_PAD6:
case EVT_PAD7:
case EVT_PAD8:
case EVT_PAD9:
case EVT_PADPLUSKEY:
case EVT_PADMINUS:
case MIDDLEMOUSE:
case MOUSEMOVE:
// pld->redraw = PL_PREVIEW_REDRAWHEADER;
ret = OPERATOR_PASS_THROUGH;
break;
/* quicky compare to original */
case EVT_TABKEY:
pld->flag &= ~PL_PREVIEW_SHOWORIGINAL;
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
break;
}
/* EXITS HERE... */
return ret;
}
/* NORMAL EVENT HANDLING... */
/* searching takes priority over normal activity */
switch (event->type) {
/* exit - cancel */
case EVT_ESCKEY:
case RIGHTMOUSE:
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_CANCEL;
break;
/* exit - confirm */
case LEFTMOUSE:
case EVT_RETKEY:
case EVT_PADENTER:
case EVT_SPACEKEY:
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_CONFIRM;
break;
/* toggle between original pose and poselib pose*/
case EVT_TABKEY:
pld->flag |= PL_PREVIEW_SHOWORIGINAL;
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
break;
/* change to previous pose (cyclic) */
case EVT_PAGEUPKEY:
case WHEELUPMOUSE:
poselib_preview_get_next(pld, -1);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
break;
/* change to next pose (cyclic) */
case EVT_PAGEDOWNKEY:
case WHEELDOWNMOUSE:
poselib_preview_get_next(pld, 1);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
break;
/* jump 5 poses (cyclic, back) */
case EVT_DOWNARROWKEY:
poselib_preview_get_next(pld, -5);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
break;
/* jump 5 poses (cyclic, forward) */
case EVT_UPARROWKEY:
poselib_preview_get_next(pld, 5);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
break;
/* change to next pose or searching cursor control */
case EVT_RIGHTARROWKEY:
if (pld->searchstr[0]) {
/* move text-cursor to the right */
2019-04-22 09:19:45 +10:00
if (pld->search_cursor < strlen(pld->searchstr)) {
pld->search_cursor++;
2019-04-22 09:19:45 +10:00
}
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWHEADER;
}
else {
/* change to next pose (cyclic) */
poselib_preview_get_next(pld, 1);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
}
break;
/* change to next pose or searching cursor control */
case EVT_LEFTARROWKEY:
if (pld->searchstr[0]) {
/* move text-cursor to the left */
2019-04-22 09:19:45 +10:00
if (pld->search_cursor) {
pld->search_cursor--;
2019-04-22 09:19:45 +10:00
}
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWHEADER;
}
else {
/* change to previous pose (cyclic) */
poselib_preview_get_next(pld, -1);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
}
break;
/* change to first pose or start of searching string */
case EVT_HOMEKEY:
if (pld->searchstr[0]) {
2012-05-08 20:18:33 +00:00
pld->search_cursor = 0;
pld->redraw = PL_PREVIEW_REDRAWHEADER;
}
else {
/* change to first pose */
2012-05-08 20:18:33 +00:00
pld->marker = pld->act->markers.first;
pld->act->active_marker = 1;
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
}
break;
/* change to last pose or start of searching string */
case EVT_ENDKEY:
if (pld->searchstr[0]) {
2012-05-08 20:18:33 +00:00
pld->search_cursor = strlen(pld->searchstr);
pld->redraw = PL_PREVIEW_REDRAWHEADER;
}
else {
/* change to last pose */
2012-05-08 20:18:33 +00:00
pld->marker = pld->act->markers.last;
pld->act->active_marker = BLI_listbase_count(&pld->act->markers);
2012-05-08 20:18:33 +00:00
pld->redraw = PL_PREVIEW_REDRAWALL;
}
break;
/* view manipulation */
/* we add pass through here, so that the operators responsible for these can still run,
* even though we still maintain control (as RUNNING_MODAL flag is still set too)
*/
case MIDDLEMOUSE:
case MOUSEMOVE:
// pld->redraw = PL_PREVIEW_REDRAWHEADER;
ret = OPERATOR_PASS_THROUGH;
break;
/* view manipulation, or searching */
case EVT_PAD0:
case EVT_PAD1:
case EVT_PAD2:
case EVT_PAD3:
case EVT_PAD4:
case EVT_PAD5:
case EVT_PAD6:
case EVT_PAD7:
case EVT_PAD8:
case EVT_PAD9:
case EVT_PADPLUSKEY:
case EVT_PADMINUS:
if (pld->searchstr[0]) {
/* searching... */
poselib_preview_handle_search(pld, event->type, event->ascii);
}
else {
/* view manipulation (see above) */
// pld->redraw = PL_PREVIEW_REDRAWHEADER;
ret = OPERATOR_PASS_THROUGH;
}
break;
/* otherwise, assume that searching might be able to handle it */
default:
poselib_preview_handle_search(pld, event->type, event->ascii);
break;
}
return ret;
}
/* ---------------------------- */
/* Init PoseLib Previewing data */
2012-05-08 20:18:33 +00:00
static void poselib_preview_init_data(bContext *C, wmOperator *op)
{
tPoseLib_PreviewData *pld;
2012-05-08 20:18:33 +00:00
Object *ob = get_poselib_object(C);
int pose_index = RNA_int_get(op->ptr, "pose_index");
/* set up preview state info */
2012-05-08 20:18:33 +00:00
op->customdata = pld = MEM_callocN(sizeof(tPoseLib_PreviewData), "PoseLib Preview Data");
/* get basic data */
2012-05-08 20:18:33 +00:00
pld->ob = ob;
pld->arm = (ob) ? (ob->data) : NULL;
pld->pose = (ob) ? (ob->pose) : NULL;
pld->act = (ob) ? (ob->poselib) : NULL;
2012-05-08 20:18:33 +00:00
pld->scene = CTX_data_scene(C);
pld->area = CTX_wm_area(C);
/* get starting pose based on RNA-props for this operator */
2019-04-22 09:19:45 +10:00
if (pose_index == -1) {
2012-05-08 20:18:33 +00:00
pld->marker = poselib_get_active_pose(pld->act);
2019-04-22 09:19:45 +10:00
}
else if (pose_index == -2) {
pld->flag |= PL_PREVIEW_SHOWORIGINAL;
2019-04-22 09:19:45 +10:00
}
else {
2012-05-08 20:18:33 +00:00
pld->marker = (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL;
2019-04-22 09:19:45 +10:00
}
/* check if valid poselib */
if (ELEM(NULL, pld->ob, pld->pose, pld->arm)) {
BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode");
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_ERROR;
return;
}
if (pld->act == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object does not have a valid pose lib");
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_ERROR;
return;
}
if (pld->marker == NULL) {
if (pld->act->markers.first) {
/* just use first one then... */
2012-05-08 20:18:33 +00:00
pld->marker = pld->act->markers.first;
2019-04-22 09:19:45 +10:00
if (pose_index > -2) {
BKE_report(op->reports, RPT_WARNING, "Pose lib had no active pose");
2019-04-22 09:19:45 +10:00
}
}
else {
BKE_report(op->reports, RPT_ERROR, "Pose lib has no poses to preview/apply");
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_ERROR;
return;
}
}
/* get ID pointer for applying poses */
RNA_id_pointer_create(&ob->id, &pld->rna_ptr);
/* make backups for restoring pose */
poselib_backup_posecopy(pld);
/* set flags for running */
2012-05-08 20:18:33 +00:00
pld->state = PL_PREVIEW_RUNNING;
pld->redraw = PL_PREVIEW_REDRAWALL;
pld->flag |= PL_PREVIEW_FIRSTTIME;
/* set depsgraph flags */
2012-05-08 20:18:33 +00:00
/* make sure the lock is set OK, unlock can be accidentally saved? */
pld->pose->flag |= POSE_LOCKED;
pld->pose->flag &= ~POSE_DO_UNLOCK;
/* clear strings + search */
2012-05-08 20:18:33 +00:00
pld->headerstr[0] = pld->searchstr[0] = pld->searchold[0] = '\0';
pld->search_cursor = 0;
}
/* After previewing poses */
2012-05-08 20:18:33 +00:00
static void poselib_preview_cleanup(bContext *C, wmOperator *op)
{
2012-05-08 20:18:33 +00:00
tPoseLib_PreviewData *pld = (tPoseLib_PreviewData *)op->customdata;
Scene *scene = pld->scene;
Object *ob = pld->ob;
bPose *pose = pld->pose;
bAction *act = pld->act;
TimeMarker *marker = pld->marker;
/* redraw the header so that it doesn't show any of our stuff anymore */
ED_area_status_text(pld->area, NULL);
ED_workspace_status_text(C, NULL);
/* this signal does one recalc on pose, then unlocks, so ESC or edit will work */
pose->flag |= POSE_DO_UNLOCK;
2012-03-08 04:12:11 +00:00
/* clear pose if canceled */
if (pld->state == PL_PREVIEW_CANCEL) {
poselib_backup_restore(pld);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
else if (pld->state == PL_PREVIEW_CONFIRM) {
/* tag poses as appropriate */
poselib_keytag_pose(C, scene, pld);
/* change active pose setting */
2012-05-08 20:18:33 +00:00
act->active_marker = BLI_findindex(&act->markers, marker) + 1;
action_set_activemarker(act, marker, NULL);
/* Update event for pose and deformation children */
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
/* updates */
if (IS_AUTOKEY_MODE(scene, NORMAL)) {
// remake_action_ipos(ob->action);
}
}
/* Request final redraw of the view. */
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob);
/* free memory used for backups and searching */
poselib_backup_free_data(pld);
BLI_freelistN(&pld->searchp);
/* free temp data for operator */
MEM_freeN(pld);
2012-05-08 20:18:33 +00:00
op->customdata = NULL;
}
/* End previewing operation */
2012-05-08 20:18:33 +00:00
static int poselib_preview_exit(bContext *C, wmOperator *op)
{
2012-05-08 20:18:33 +00:00
tPoseLib_PreviewData *pld = op->customdata;
int exit_state = pld->state;
/* finish up */
poselib_preview_cleanup(C, op);
2019-04-22 09:19:45 +10:00
if (ELEM(exit_state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR)) {
return OPERATOR_CANCELLED;
2019-04-22 09:19:45 +10:00
}
return OPERATOR_FINISHED;
}
/* Cancel previewing operation (called when exiting Blender) */
static void poselib_preview_cancel(bContext *C, wmOperator *op)
{
poselib_preview_exit(C, op);
}
/* main modal status check */
static int poselib_preview_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
2012-05-08 20:18:33 +00:00
tPoseLib_PreviewData *pld = op->customdata;
int ret;
/* 1) check state to see if we're still running */
2019-04-22 09:19:45 +10:00
if (pld->state != PL_PREVIEW_RUNNING) {
return poselib_preview_exit(C, op);
2019-04-22 09:19:45 +10:00
}
/* 2) handle events */
2012-05-08 20:18:33 +00:00
ret = poselib_preview_handle_event(C, op, event);
/* 3) apply changes and redraw, otherwise, confirming goes wrong */
2019-04-22 09:19:45 +10:00
if (pld->redraw) {
poselib_preview_apply(C, op);
2019-04-22 09:19:45 +10:00
}
return ret;
}
/* Modal Operator init */
static int poselib_preview_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
tPoseLib_PreviewData *pld;
/* check if everything is ok, and init settings for modal operator */
poselib_preview_init_data(C, op);
2012-05-08 20:18:33 +00:00
pld = (tPoseLib_PreviewData *)op->customdata;
if (pld->state == PL_PREVIEW_ERROR) {
/* an error occurred, so free temp mem used */
poselib_preview_cleanup(C, op);
return OPERATOR_CANCELLED;
}
/* do initial apply to have something to look at */
poselib_preview_apply(C, op);
/* add temp handler if we're running as a modal operator */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
/* Repeat operator */
2012-05-08 20:18:33 +00:00
static int poselib_preview_exec(bContext *C, wmOperator *op)
{
tPoseLib_PreviewData *pld;
/* check if everything is ok, and init settings for modal operator */
poselib_preview_init_data(C, op);
2012-05-08 20:18:33 +00:00
pld = (tPoseLib_PreviewData *)op->customdata;
if (pld->state == PL_PREVIEW_ERROR) {
/* an error occurred, so free temp mem used */
poselib_preview_cleanup(C, op);
return OPERATOR_CANCELLED;
}
/* the exec() callback is effectively a 'run-once' scenario, so set the state to that
* so that everything draws correctly
*/
pld->state = PL_PREVIEW_RUNONCE;
/* apply the active pose */
poselib_preview_apply(C, op);
/* now, set the status to exit */
pld->state = PL_PREVIEW_CONFIRM;
/* cleanup */
return poselib_preview_exit(C, op);
}
void POSELIB_OT_browse_interactive(wmOperatorType *ot)
{
/* identifiers */
ot->name = "PoseLib Browse Poses";
ot->idname = "POSELIB_OT_browse_interactive";
ot->description = "Interactively browse poses in 3D-View";
/* callbacks */
ot->invoke = poselib_preview_invoke;
ot->modal = poselib_preview_modal;
ot->cancel = poselib_preview_cancel;
ot->exec = poselib_preview_exec;
ot->poll = has_poselib_pose_data_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
/* properties */
2012-05-08 20:18:33 +00:00
/* TODO: make the pose_index into a proper enum instead of a cryptic int. */
ot->prop = RNA_def_int(
ot->srna,
"pose_index",
-1,
-2,
INT_MAX,
"Pose",
"Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)",
0,
INT_MAX);
/* XXX: percentage vs factor? */
/* not used yet */
#if 0
RNA_def_float_factor(ot->srna,
"blend_factor",
1.0f,
0.0f,
1.0f,
"Blend Factor",
"Amount that the pose is applied on top of the existing poses",
0.0f,
1.0f);
#endif
}
void POSELIB_OT_apply_pose(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Apply Pose Library Pose";
ot->idname = "POSELIB_OT_apply_pose";
ot->description = "Apply specified Pose Library pose to the rig";
/* callbacks */
ot->exec = poselib_preview_exec;
ot->poll = has_poselib_pose_data_poll;
/* flags */
2012-05-08 20:18:33 +00:00
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
/* TODO: make the pose_index into a proper enum instead of a cryptic int... */
ot->prop = RNA_def_int(
ot->srna,
"pose_index",
-1,
-2,
INT_MAX,
"Pose",
"Index of the pose to apply (-2 for no change to pose, -1 for poselib active pose)",
0,
INT_MAX);
}