2009-01-13 12:32:01 +00:00
|
|
|
/**
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2007, Blender Foundation
|
|
|
|
* This is a new part of Blender
|
|
|
|
*
|
|
|
|
* Contributor(s): Joshua Leung
|
|
|
|
*
|
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <float.h>
|
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_arithb.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
|
|
#include "BLI_dynstr.h"
|
2009-07-18 07:11:37 +00:00
|
|
|
#include "BLI_dlrbTree.h"
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
#include "DNA_listBase.h"
|
2009-04-15 01:10:36 +00:00
|
|
|
#include "DNA_anim_types.h"
|
2009-01-13 12:32:01 +00:00
|
|
|
#include "DNA_action_types.h"
|
|
|
|
#include "DNA_armature_types.h"
|
|
|
|
#include "DNA_curve_types.h"
|
|
|
|
#include "DNA_object_types.h"
|
|
|
|
#include "DNA_object_force.h"
|
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
#include "DNA_userdef_types.h"
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
#include "BKE_animsys.h"
|
2009-01-13 12:32:01 +00:00
|
|
|
#include "BKE_action.h"
|
|
|
|
#include "BKE_armature.h"
|
|
|
|
#include "BKE_depsgraph.h"
|
|
|
|
#include "BKE_modifier.h"
|
|
|
|
#include "BKE_object.h"
|
|
|
|
|
|
|
|
#include "BKE_global.h"
|
2009-04-15 03:22:22 +00:00
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "BKE_report.h"
|
2009-01-13 12:32:01 +00:00
|
|
|
#include "BKE_utildefines.h"
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
#include "RNA_access.h"
|
2009-04-15 03:22:22 +00:00
|
|
|
#include "RNA_define.h"
|
2009-04-15 01:10:36 +00:00
|
|
|
#include "RNA_types.h"
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
#include "WM_api.h"
|
2009-01-13 12:32:01 +00:00
|
|
|
#include "WM_types.h"
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
#include "UI_interface.h"
|
|
|
|
#include "UI_resources.h"
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
#include "ED_anim_api.h"
|
|
|
|
#include "ED_armature.h"
|
|
|
|
#include "ED_keyframes_draw.h"
|
|
|
|
#include "ED_keyframing.h"
|
|
|
|
#include "ED_keyframes_edit.h"
|
2009-04-15 03:22:22 +00:00
|
|
|
#include "ED_screen.h"
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
#include "armature_intern.h"
|
|
|
|
|
|
|
|
/* ******* XXX ********** */
|
|
|
|
|
|
|
|
static void BIF_undo_push() {}
|
|
|
|
static void error() {}
|
|
|
|
|
|
|
|
static void action_set_activemarker() {}
|
|
|
|
|
|
|
|
/* ************************************************************* */
|
|
|
|
/* == POSE-LIBRARY TOOL FOR BLENDER ==
|
|
|
|
*
|
|
|
|
* Overview:
|
|
|
|
* 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:
|
|
|
|
* - PoseLibs are simply normal Actions
|
|
|
|
* - Each "pose" is simply a set of keyframes that occur on a particular frame
|
|
|
|
* -> a set of TimeMarkers that belong to each Action, help 'label' where a 'pose' can be
|
|
|
|
* found in the Action
|
|
|
|
* - The Scrollwheel 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 poselib, allowing the
|
|
|
|
* animator to preview what action best suits that pose
|
|
|
|
*/
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
|
|
|
/* gets list of poses in poselib as a string usable for pupmenu() */
|
|
|
|
char *poselib_build_poses_menu (bAction *act, char title[])
|
|
|
|
{
|
|
|
|
DynStr *pupds= BLI_dynstr_new();
|
|
|
|
TimeMarker *marker;
|
|
|
|
char *str;
|
|
|
|
char buf[64];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* add title first */
|
|
|
|
sprintf(buf, "%s%%t|", title);
|
|
|
|
BLI_dynstr_append(pupds, buf);
|
|
|
|
|
|
|
|
/* loop through markers, adding them */
|
|
|
|
for (marker=act->markers.first, i=1; marker; marker=marker->next, i++) {
|
|
|
|
BLI_dynstr_append(pupds, marker->name);
|
|
|
|
|
|
|
|
sprintf(buf, "%%x%d", i);
|
|
|
|
BLI_dynstr_append(pupds, buf);
|
|
|
|
|
|
|
|
if (marker->next)
|
|
|
|
BLI_dynstr_append(pupds, "|");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert to normal MEM_malloc'd string */
|
|
|
|
str= BLI_dynstr_get_cstring(pupds);
|
|
|
|
BLI_dynstr_free(pupds);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* gets the first available frame in poselib to store a pose on
|
|
|
|
* - frames start from 1, and a pose should occur on every frame... 0 is error!
|
|
|
|
*/
|
|
|
|
int poselib_get_free_index (bAction *act)
|
|
|
|
{
|
|
|
|
TimeMarker *marker;
|
|
|
|
int low=0, high=0;
|
|
|
|
|
|
|
|
/* sanity checks */
|
|
|
|
if (ELEM(NULL, act, act->markers.first)) return 1;
|
|
|
|
|
|
|
|
/* loop over poses finding various values (poses are not stored in chronological order) */
|
|
|
|
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++;
|
|
|
|
|
|
|
|
/* value replaces high if it is the highest value encountered yet */
|
|
|
|
if (marker->frame > high)
|
|
|
|
high= marker->frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* - 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)
|
|
|
|
*/
|
|
|
|
if (low < high)
|
|
|
|
return (low + 1);
|
|
|
|
else
|
|
|
|
return (high + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* returns the active pose for a poselib */
|
|
|
|
TimeMarker *poselib_get_active_pose (bAction *act)
|
|
|
|
{
|
|
|
|
if ((act) && (act->active_marker))
|
|
|
|
return BLI_findlink(&act->markers, act->active_marker-1);
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
|
|
|
/* Initialise a new poselib (whether it is needed or not) */
|
|
|
|
bAction *poselib_init_new (Object *ob)
|
|
|
|
{
|
|
|
|
/* sanity checks - only for armatures */
|
|
|
|
if (ELEM(NULL, ob, ob->pose))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* init object's poselib action (unlink old one if there) */
|
|
|
|
if (ob->poselib)
|
|
|
|
ob->poselib->id.us--;
|
2009-04-13 11:15:43 +00:00
|
|
|
ob->poselib= add_empty_action("PoseLib");
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
return ob->poselib;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialise a new poselib (checks if that needs to happen) */
|
|
|
|
bAction *poselib_validate (Object *ob)
|
|
|
|
{
|
|
|
|
if (ELEM(NULL, ob, ob->pose))
|
|
|
|
return NULL;
|
|
|
|
else if (ob->poselib == NULL)
|
|
|
|
return poselib_init_new(ob);
|
|
|
|
else
|
|
|
|
return ob->poselib;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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.
|
|
|
|
*/
|
2009-04-15 12:04:10 +00:00
|
|
|
// TODO: operatorfy me!
|
2009-01-13 12:32:01 +00:00
|
|
|
void poselib_validate_act (bAction *act)
|
|
|
|
{
|
2009-07-18 07:11:37 +00:00
|
|
|
DLRBT_Tree keys = {NULL, NULL};
|
2009-01-13 12:32:01 +00:00
|
|
|
ActKeyColumn *ak;
|
|
|
|
TimeMarker *marker, *markern;
|
|
|
|
|
|
|
|
/* validate action and poselib */
|
|
|
|
if (act == NULL) {
|
|
|
|
error("No Action to validate");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* determine which frames have keys */
|
2009-07-18 07:11:37 +00:00
|
|
|
BLI_dlrbTree_init(&keys);
|
2009-07-09 13:14:51 +00:00
|
|
|
action_to_keylist(NULL, act, &keys, NULL);
|
2009-07-18 07:11:37 +00:00
|
|
|
BLI_dlrbTree_linkedlist_sync(&keys);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* for each key, make sure there is a correspnding pose */
|
|
|
|
for (ak= keys.first; ak; ak= ak->next) {
|
|
|
|
/* check if any pose matches this */
|
|
|
|
for (marker= act->markers.first; marker; marker= marker->next) {
|
|
|
|
if (IS_EQ(marker->frame, ak->cfra)) {
|
|
|
|
marker->flag = -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add new if none found */
|
|
|
|
if (marker == NULL) {
|
|
|
|
char name[64];
|
|
|
|
|
|
|
|
/* add pose to poselib */
|
|
|
|
marker= MEM_callocN(sizeof(TimeMarker), "ActionMarker");
|
|
|
|
|
|
|
|
strcpy(name, "Pose");
|
|
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
|
|
|
|
|
|
marker->frame= (int)ak->cfra;
|
|
|
|
marker->flag= -1;
|
|
|
|
|
|
|
|
BLI_addtail(&act->markers, marker);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove all untagged poses (unused), and remove all tags */
|
|
|
|
for (marker= act->markers.first; marker; marker= markern) {
|
|
|
|
markern= marker->next;
|
|
|
|
|
|
|
|
if (marker->flag != -1)
|
|
|
|
BLI_freelinkN(&act->markers, marker);
|
|
|
|
else
|
|
|
|
marker->flag = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free temp memory */
|
2009-07-19 05:20:30 +00:00
|
|
|
BLI_freelistN((ListBase *)&keys);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
BIF_undo_push("PoseLib Validate Action");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* Pointers to the builtin KeyingSets that we want to use */
|
2009-09-13 12:34:00 +00:00
|
|
|
static KeyingSet *poselib_ks_locrotscale = NULL; /* the only keyingset we'll need */
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* ----- */
|
|
|
|
|
2009-04-22 18:39:44 +00:00
|
|
|
static void poselib_add_menu_invoke__replacemenu (bContext *C, uiLayout *layout, void *arg)
|
2009-04-15 03:22:22 +00:00
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
bAction *act= ob->poselib;
|
|
|
|
TimeMarker *marker;
|
|
|
|
|
|
|
|
/* add each marker to this menu */
|
|
|
|
for (marker= act->markers.first; marker; marker= marker->next)
|
2009-04-22 18:39:44 +00:00
|
|
|
uiItemIntO(layout, marker->name, ICON_ARMATURE_DATA, "POSELIB_OT_pose_add", "frame", marker->frame);
|
2009-04-15 03:22:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int poselib_add_menu_invoke (bContext *C, wmOperator *op, wmEvent *evt)
|
|
|
|
{
|
|
|
|
Scene *scene= CTX_data_scene(C);
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
bArmature *arm= (ob) ? ob->data : NULL;
|
|
|
|
bPose *pose= (ob) ? ob->pose : NULL;
|
2009-04-22 18:39:44 +00:00
|
|
|
uiPopupMenu *pup;
|
|
|
|
uiLayout *layout;
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (ELEM3(NULL, ob, arm, pose))
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
/* start building */
|
UI: Layout Engine
* Buttons are now created first, and after that the layout is computed.
This means the layout engine now works at button level, and makes it
easier to write templates. Otherwise you had to store all info and
create the buttons later.
* Added interface_templates.c as a separate file to put templates in.
These can contain regular buttons, and can be put in a Free layout,
which means you can specify manual coordinates, but still get nested
correct inside other layouts.
* API was changed to allow better nesting. Previously items were added
in the last added layout specifier, i.e. one level up in the layout
hierarchy. This doesn't work well in always, so now when creating things
like rows or columns it always returns a layout which you have to add
the items in. All py scripts were updated to follow this.
* Computing the layout now goes in two passes, first estimating the
required width/height of all nested layouts, and then in the second
pass using the results of that to decide on the actual locations.
* Enum and array buttons now follow the direction of the layout, i.e.
they are vertical or horizontal depending if they are in a column or row.
* Color properties now get a color picker, and only get the additional
RGB sliders with Expand=True.
* File/directory string properties now get a button next to them for
opening the file browse, though this is not implemented yet.
* Layout items can now be aligned, set align=True when creating a column,
row, etc.
* Buttons now get a minimum width of one icon (avoids squashing icon
buttons).
* Moved some more space variables into Style.
2009-05-15 11:19:59 +00:00
|
|
|
pup= uiPupMenuBegin(C, op->type->name, 0);
|
2009-04-22 18:39:44 +00:00
|
|
|
layout= uiPupMenuLayout(pup);
|
2009-05-28 23:37:55 +00:00
|
|
|
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* add new (adds to the first unoccupied frame) */
|
2009-04-22 18:39:44 +00:00
|
|
|
uiItemIntO(layout, "Add New", 0, "POSELIB_OT_pose_add", "frame", poselib_get_free_index(ob->poselib));
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* 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) */
|
2009-04-22 18:39:44 +00:00
|
|
|
uiItemIntO(layout, "Add New (Current Frame)", 0, "POSELIB_OT_pose_add", "frame", CFRA);
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* replace existing - submenu */
|
2009-08-19 00:55:30 +00:00
|
|
|
uiItemMenuF(layout, "Replace Existing...", 0, poselib_add_menu_invoke__replacemenu, NULL);
|
2009-04-15 03:22:22 +00:00
|
|
|
}
|
|
|
|
|
2009-04-22 18:39:44 +00:00
|
|
|
uiPupMenuEnd(C, pup);
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* this operator is only for a menu, not used further */
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int poselib_add_exec (bContext *C, wmOperator *op)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-10-08 05:53:26 +00:00
|
|
|
Scene *scene= CTX_data_scene(C);
|
2009-04-15 03:22:22 +00:00
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
bAction *act = poselib_validate(ob);
|
2009-01-13 12:32:01 +00:00
|
|
|
bArmature *arm= (ob) ? ob->data : NULL;
|
|
|
|
bPose *pose= (ob) ? ob->pose : NULL;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
TimeMarker *marker;
|
2009-04-15 03:22:22 +00:00
|
|
|
int frame= RNA_int_get(op->ptr, "frame");
|
2009-01-13 12:32:01 +00:00
|
|
|
char name[64];
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
bCommonKeySrc cks;
|
|
|
|
ListBase dsources = {&cks, &cks};
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* sanity check (invoke should have checked this anyway) */
|
2009-01-13 12:32:01 +00:00
|
|
|
if (ELEM3(NULL, ob, arm, pose))
|
2009-04-15 03:22:22 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* get name to give to pose */
|
|
|
|
RNA_string_get(op->ptr, "name", name);
|
|
|
|
|
|
|
|
/* add pose to poselib - replaces any existing pose there
|
|
|
|
* - for the 'replace' option, this should end up finding the appropriate marker,
|
|
|
|
* so no new one will be added
|
|
|
|
*/
|
|
|
|
for (marker= act->markers.first; marker; marker= marker->next) {
|
|
|
|
if (marker->frame == frame) {
|
|
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
|
|
break;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-15 03:22:22 +00:00
|
|
|
if (marker == NULL) {
|
|
|
|
marker= MEM_callocN(sizeof(TimeMarker), "ActionMarker");
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
BLI_strncpy(marker->name, name, sizeof(marker->name));
|
|
|
|
marker->frame= frame;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
BLI_addtail(&act->markers, marker);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* validate name */
|
2009-04-16 13:10:08 +00:00
|
|
|
BLI_uniquename(&act->markers, marker, "Pose", '.', offsetof(TimeMarker, name), 64);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* init common-key-source for use by KeyingSets */
|
|
|
|
memset(&cks, 0, sizeof(bCommonKeySrc));
|
|
|
|
cks.id= &ob->id;
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* loop through selected posechannels, keying their pose to the action */
|
|
|
|
for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
|
|
/* check if available */
|
|
|
|
if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
|
|
|
|
if (pchan->bone->flag & (BONE_SELECTED|BONE_ACTIVE)) {
|
2009-04-15 01:10:36 +00:00
|
|
|
/* init cks for this PoseChannel, then use the relative KeyingSets to keyframe it */
|
|
|
|
cks.pchan= pchan;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-09-11 12:44:09 +00:00
|
|
|
/* KeyingSet to use depends on rotation mode (but that's handled by the templates code) */
|
2009-09-13 12:34:00 +00:00
|
|
|
if (poselib_ks_locrotscale == NULL)
|
|
|
|
poselib_ks_locrotscale= ANIM_builtin_keyingset_get_named(NULL, "LocRotScale");
|
2009-10-08 05:53:26 +00:00
|
|
|
modify_keyframes(scene, &dsources, act, poselib_ks_locrotscale, MODIFYKEY_MODE_INSERT, (float)frame);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* store new 'active' pose number */
|
|
|
|
act->active_marker= BLI_countlist(&act->markers);
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* done */
|
|
|
|
return OPERATOR_FINISHED;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
void POSELIB_OT_pose_add (wmOperatorType *ot)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 03:22:22 +00:00
|
|
|
/* 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= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ----- */
|
|
|
|
|
|
|
|
static int poselib_stored_pose_menu_invoke (bContext *C, wmOperator *op, wmEvent *evt)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
|
|
|
bAction *act= (ob) ? ob->poselib : NULL;
|
|
|
|
TimeMarker *marker;
|
2009-04-22 18:39:44 +00:00
|
|
|
uiPopupMenu *pup;
|
|
|
|
uiLayout *layout;
|
2009-04-15 03:22:22 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (ELEM(NULL, ob, act))
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
/* start building */
|
UI: Layout Engine
* Buttons are now created first, and after that the layout is computed.
This means the layout engine now works at button level, and makes it
easier to write templates. Otherwise you had to store all info and
create the buttons later.
* Added interface_templates.c as a separate file to put templates in.
These can contain regular buttons, and can be put in a Free layout,
which means you can specify manual coordinates, but still get nested
correct inside other layouts.
* API was changed to allow better nesting. Previously items were added
in the last added layout specifier, i.e. one level up in the layout
hierarchy. This doesn't work well in always, so now when creating things
like rows or columns it always returns a layout which you have to add
the items in. All py scripts were updated to follow this.
* Computing the layout now goes in two passes, first estimating the
required width/height of all nested layouts, and then in the second
pass using the results of that to decide on the actual locations.
* Enum and array buttons now follow the direction of the layout, i.e.
they are vertical or horizontal depending if they are in a column or row.
* Color properties now get a color picker, and only get the additional
RGB sliders with Expand=True.
* File/directory string properties now get a button next to them for
opening the file browse, though this is not implemented yet.
* Layout items can now be aligned, set align=True when creating a column,
row, etc.
* Buttons now get a minimum width of one icon (avoids squashing icon
buttons).
* Moved some more space variables into Style.
2009-05-15 11:19:59 +00:00
|
|
|
pup= uiPupMenuBegin(C, op->type->name, 0);
|
2009-04-22 18:39:44 +00:00
|
|
|
layout= uiPupMenuLayout(pup);
|
2009-05-28 23:37:55 +00:00
|
|
|
uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT);
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* add each marker to this menu */
|
|
|
|
for (marker=act->markers.first, i=0; marker; marker= marker->next, i++)
|
2009-04-22 18:39:44 +00:00
|
|
|
uiItemIntO(layout, marker->name, ICON_ARMATURE_DATA, op->idname, "index", i);
|
2009-04-15 03:22:22 +00:00
|
|
|
|
2009-04-22 18:39:44 +00:00
|
|
|
uiPupMenuEnd(C, pup);
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
/* this operator is only for a menu, not used further */
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int poselib_remove_exec (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
2009-01-13 12:32:01 +00:00
|
|
|
bAction *act= (ob) ? ob->poselib : NULL;
|
2009-04-15 03:22:22 +00:00
|
|
|
TimeMarker *marker;
|
2009-04-15 01:10:36 +00:00
|
|
|
FCurve *fcu;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* check if valid poselib */
|
|
|
|
if (act == NULL) {
|
2009-04-15 03:22:22 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Object doesn't have PoseLib data");
|
|
|
|
return OPERATOR_CANCELLED;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* get index (and pointer) of pose to remove */
|
2009-04-15 03:22:22 +00:00
|
|
|
marker= BLI_findlink(&act->markers, RNA_int_get(op->ptr, "index"));
|
2009-01-13 12:32:01 +00:00
|
|
|
if (marker == NULL) {
|
2009-04-15 03:22:22 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Invalid index for Pose");
|
2009-09-20 11:21:44 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* remove relevant keyframes */
|
2009-04-15 01:10:36 +00:00
|
|
|
for (fcu= act->curves.first; fcu; fcu= fcu->next) {
|
2009-01-13 12:32:01 +00:00
|
|
|
BezTriple *bezt;
|
|
|
|
int i;
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
if (fcu->bezt) {
|
|
|
|
for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
|
|
|
/* check if remove */
|
2009-01-13 12:32:01 +00:00
|
|
|
if (IS_EQ(bezt->vec[1][0], marker->frame)) {
|
2009-04-15 01:10:36 +00:00
|
|
|
delete_fcurve_key(fcu, i, 1);
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-04-15 01:10:36 +00:00
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove poselib from list */
|
|
|
|
BLI_freelinkN(&act->markers, marker);
|
|
|
|
|
|
|
|
/* fix active pose number */
|
|
|
|
act->active_marker= 0;
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* done */
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
|
|
|
|
void POSELIB_OT_pose_remove (wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* 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= poselib_stored_pose_menu_invoke;
|
|
|
|
ot->exec= poselib_remove_exec;
|
|
|
|
ot->poll= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "The index of the pose to remove", 0, INT_MAX);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
|
|
|
|
static int poselib_rename_exec (bContext *C, wmOperator *op)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 03:22:22 +00:00
|
|
|
Object *ob= CTX_data_active_object(C);
|
2009-01-13 12:32:01 +00:00
|
|
|
bAction *act= (ob) ? ob->poselib : NULL;
|
|
|
|
TimeMarker *marker;
|
2009-04-15 03:22:22 +00:00
|
|
|
char newname[64];
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* check if valid poselib */
|
|
|
|
if (act == NULL) {
|
2009-04-15 03:22:22 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Object doesn't have PoseLib data");
|
|
|
|
return OPERATOR_CANCELLED;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* get index (and pointer) of pose to remove */
|
|
|
|
marker= BLI_findlink(&act->markers, RNA_int_get(op->ptr, "index"));
|
|
|
|
if (marker == NULL) {
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Invalid index for Pose");
|
2009-09-20 11:21:44 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-04-15 03:22:22 +00:00
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* get new name */
|
|
|
|
RNA_string_get(op->ptr, "name", newname);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* copy name and validate it */
|
2009-04-15 03:22:22 +00:00
|
|
|
BLI_strncpy(marker->name, newname, sizeof(marker->name));
|
2009-04-16 13:10:08 +00:00
|
|
|
BLI_uniquename(&act->markers, marker, "Pose", '.', offsetof(TimeMarker, name), 64);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* done */
|
|
|
|
return OPERATOR_FINISHED;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
void POSELIB_OT_pose_rename (wmOperatorType *ot)
|
|
|
|
{
|
|
|
|
/* identifiers */
|
|
|
|
ot->name= "PoseLib Rename Pose";
|
|
|
|
ot->idname= "POSELIB_OT_pose_rename";
|
|
|
|
ot->description= "Rename nth pose from the active Pose Library";
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
ot->invoke= poselib_stored_pose_menu_invoke;
|
|
|
|
ot->exec= poselib_rename_exec;
|
|
|
|
ot->poll= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "The index of the pose to remove", 0, INT_MAX);
|
|
|
|
RNA_def_string(ot->srna, "name", "RenamedPose", 64, "New Pose Name", "New name for pose");
|
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
|
|
|
/* Simple struct for storing settings/data for use during PoseLib preview */
|
|
|
|
typedef struct tPoseLib_PreviewData {
|
|
|
|
ListBase backups; /* tPoseLib_Backup structs for restoring poses */
|
|
|
|
ListBase searchp; /* LinkData structs storing list of poses which match the current search-string */
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
Scene *scene; /* active scene */
|
|
|
|
ScrArea *sa; /* active area */
|
|
|
|
|
|
|
|
PointerRNA rna_ptr; /* RNA-Pointer to Object 'ob' */
|
2009-01-13 12:32:01 +00:00
|
|
|
Object *ob; /* object to work on */
|
|
|
|
bArmature *arm; /* object's armature data */
|
|
|
|
bPose *pose; /* object's pose */
|
|
|
|
bAction *act; /* poselib to use */
|
|
|
|
TimeMarker *marker; /* 'active' pose */
|
|
|
|
|
|
|
|
short state; /* state of main loop */
|
|
|
|
short redraw; /* redraw/update settings during main loop */
|
|
|
|
short flag; /* flags for various settings */
|
|
|
|
|
|
|
|
int selcount; /* number of selected elements to work on */
|
|
|
|
int totcount; /* total number of elements to work on */
|
|
|
|
|
|
|
|
char headerstr[200]; /* Info-text to print in header */
|
|
|
|
|
|
|
|
char searchstr[64]; /* (Part of) Name to search for to filter poses that get shown */
|
|
|
|
char searchold[64]; /* Previously set searchstr (from last loop run), so that we can detected when to rebuild searchp */
|
|
|
|
short search_cursor; /* position of cursor in searchstr (cursor occurs before the item at the nominated index) */
|
|
|
|
} 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 {
|
|
|
|
PL_PREVIEW_FIRSTTIME = (1<<0),
|
|
|
|
PL_PREVIEW_SHOWORIGINAL = (1<<1)
|
|
|
|
};
|
|
|
|
|
|
|
|
/* ---------------------------- */
|
|
|
|
|
|
|
|
/* simple struct for storing backup info */
|
|
|
|
typedef struct tPoseLib_Backup {
|
|
|
|
struct tPoseLib_Backup *next, *prev;
|
|
|
|
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
bPoseChannel olddata;
|
|
|
|
} tPoseLib_Backup;
|
|
|
|
|
|
|
|
/* Makes a copy of the current pose for restoration purposes - doesn't do constraints currently */
|
|
|
|
static void poselib_backup_posecopy (tPoseLib_PreviewData *pld)
|
|
|
|
{
|
2009-04-16 00:33:40 +00:00
|
|
|
bActionGroup *agrp;
|
2009-01-13 12:32:01 +00:00
|
|
|
bPoseChannel *pchan;
|
|
|
|
|
|
|
|
/* for each posechannel that has an actionchannel in */
|
2009-04-16 00:33:40 +00:00
|
|
|
for (agrp= pld->act->groups.first; agrp; agrp= agrp->next) {
|
2009-01-13 12:32:01 +00:00
|
|
|
/* try to find posechannel */
|
2009-04-16 00:33:40 +00:00
|
|
|
pchan= get_pose_channel(pld->pose, agrp->name);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* backup data if available */
|
|
|
|
if (pchan) {
|
|
|
|
tPoseLib_Backup *plb;
|
|
|
|
|
|
|
|
/* store backup */
|
|
|
|
plb= MEM_callocN(sizeof(tPoseLib_Backup), "tPoseLib_Backup");
|
|
|
|
|
|
|
|
plb->pchan= pchan;
|
|
|
|
memcpy(&plb->olddata, plb->pchan, sizeof(bPoseChannel));
|
|
|
|
|
|
|
|
BLI_addtail(&pld->backups, plb);
|
|
|
|
|
|
|
|
/* mark as being affected */
|
|
|
|
if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED))
|
|
|
|
pld->selcount++;
|
|
|
|
pld->totcount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Restores original pose - doesn't do constraints currently */
|
|
|
|
static void poselib_backup_restore (tPoseLib_PreviewData *pld)
|
|
|
|
{
|
|
|
|
tPoseLib_Backup *plb;
|
|
|
|
|
|
|
|
for (plb= pld->backups.first; plb; plb= plb->next) {
|
|
|
|
memcpy(plb->pchan, &plb->olddata, sizeof(bPoseChannel));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------- */
|
|
|
|
|
|
|
|
/* Applies the appropriate stored pose from the pose-library to the current pose
|
|
|
|
* - 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
|
|
|
|
*/
|
|
|
|
static void poselib_apply_pose (tPoseLib_PreviewData *pld)
|
|
|
|
{
|
2009-04-15 12:04:10 +00:00
|
|
|
PointerRNA *ptr= &pld->rna_ptr;
|
2009-04-15 01:10:36 +00:00
|
|
|
bArmature *arm= pld->arm;
|
2009-01-13 12:32:01 +00:00
|
|
|
bPose *pose= pld->pose;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
bAction *act= pld->act;
|
2009-04-15 01:10:36 +00:00
|
|
|
bActionGroup *agrp;
|
|
|
|
|
|
|
|
BeztEditData bed;
|
|
|
|
BeztEditFunc group_ok_cb;
|
2009-04-15 12:04:10 +00:00
|
|
|
int frame= 1;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* get the frame */
|
2009-01-13 12:32:01 +00:00
|
|
|
if (pld->marker)
|
|
|
|
frame= pld->marker->frame;
|
|
|
|
else
|
|
|
|
return;
|
2009-04-15 12:38:04 +00:00
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* init settings for testing groups for keyframes */
|
|
|
|
group_ok_cb= ANIM_editkeyframes_ok(BEZT_OK_FRAMERANGE);
|
|
|
|
memset(&bed, 0, sizeof(BeztEditData));
|
|
|
|
bed.f1= ((float)frame) - 0.5f;
|
|
|
|
bed.f2= ((float)frame) + 0.5f;
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* start applying - only those channels which have a key at this point in time! */
|
2009-04-15 01:10:36 +00:00
|
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
|
|
|
/* check if group has any keyframes */
|
|
|
|
if (ANIM_animchanneldata_keys_bezier_loop(&bed, agrp, ALE_GROUP, NULL, group_ok_cb, NULL, 0)) {
|
|
|
|
/* has keyframe on this frame, so try to get a PoseChannel with this name */
|
|
|
|
pchan= get_pose_channel(pose, agrp->name);
|
|
|
|
|
|
|
|
if (pchan) {
|
|
|
|
short ok= 0;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* check if this bone should get any animation applied */
|
|
|
|
if (pld->selcount == 0) {
|
|
|
|
/* if no bones are selected, then any bone is ok */
|
|
|
|
ok= 1;
|
|
|
|
}
|
|
|
|
else if (pchan->bone) {
|
|
|
|
/* only ok if bone is visible and selected */
|
|
|
|
if ( (pchan->bone->flag & (BONE_SELECTED|BONE_ACTIVE)) &&
|
|
|
|
(pchan->bone->flag & BONE_HIDDEN_P)==0 &&
|
|
|
|
(pchan->bone->layer & arm->layer) )
|
|
|
|
ok = 1;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 12:38:04 +00:00
|
|
|
if (ok)
|
2009-04-15 01:10:36 +00:00
|
|
|
animsys_evaluate_action_group(ptr, act, agrp, NULL, (float)frame);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Auto-keys/tags bones affected by the pose used from the poselib */
|
2009-09-13 12:34:00 +00:00
|
|
|
static void poselib_keytag_pose (bContext *C, Scene *scene, tPoseLib_PreviewData *pld)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
|
|
|
bPose *pose= pld->pose;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
bAction *act= pld->act;
|
2009-04-15 12:04:10 +00:00
|
|
|
bActionGroup *agrp;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-09-13 12:34:00 +00:00
|
|
|
bCommonKeySrc cks;
|
|
|
|
ListBase dsources = {&cks, &cks};
|
|
|
|
|
|
|
|
/* init common-key-source for use by KeyingSets */
|
|
|
|
memset(&cks, 0, sizeof(bCommonKeySrc));
|
|
|
|
cks.id= &pld->ob->id;
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* start tagging/keying */
|
2009-04-15 12:04:10 +00:00
|
|
|
for (agrp= act->groups.first; agrp; agrp= agrp->next) {
|
2009-01-13 12:32:01 +00:00
|
|
|
/* only for selected action channels */
|
2009-04-15 12:04:10 +00:00
|
|
|
if (agrp->flag & AGRP_SELECTED) {
|
|
|
|
pchan= get_pose_channel(pose, agrp->name);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
if (pchan) {
|
|
|
|
// TODO: use a standard autokeying function in future (to allow autokeying-editkeys to work)
|
2009-09-13 12:34:00 +00:00
|
|
|
if (IS_AUTOKEY_MODE(scene, NORMAL)) {
|
|
|
|
/* Set keys on pose
|
|
|
|
* - KeyingSet to use depends on rotation mode
|
|
|
|
* (but that's handled by the templates code)
|
|
|
|
*/
|
|
|
|
// TODO: for getting the KeyingSet used, we should really check which channels were affected
|
|
|
|
if (poselib_ks_locrotscale == NULL)
|
|
|
|
poselib_ks_locrotscale= ANIM_builtin_keyingset_get_named(NULL, "LocRotScale");
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-09-19 00:18:42 +00:00
|
|
|
/* init cks for this PoseChannel, then use the relative KeyingSets to keyframe it */
|
|
|
|
cks.pchan= pchan;
|
|
|
|
|
|
|
|
/* now insert the keyframe */
|
2009-10-08 05:53:26 +00:00
|
|
|
modify_keyframes(scene, &dsources, NULL, poselib_ks_locrotscale, MODIFYKEY_MODE_INSERT, (float)CFRA);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* clear any unkeyed tags */
|
|
|
|
if (pchan->bone)
|
|
|
|
pchan->bone->flag &= ~BONE_UNKEYED;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* add unkeyed tags */
|
|
|
|
if (pchan->bone)
|
|
|
|
pchan->bone->flag |= BONE_UNKEYED;
|
|
|
|
}
|
2009-04-15 12:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-08 05:53:26 +00:00
|
|
|
|
|
|
|
/* send notifiers for this */
|
|
|
|
WM_event_add_notifier(C, NC_ANIMATION|ND_KEYFRAME_EDIT, NULL);
|
2009-04-15 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Apply the relevant changes to the pose */
|
|
|
|
static void poselib_preview_apply (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
tPoseLib_PreviewData *pld= (tPoseLib_PreviewData *)op->customdata;
|
|
|
|
|
|
|
|
/* only recalc pose (and its dependencies) if pose has changed */
|
|
|
|
if (pld->redraw == PL_PREVIEW_REDRAWALL) {
|
|
|
|
/* don't clear pose if firsttime */
|
|
|
|
if ((pld->flag & PL_PREVIEW_FIRSTTIME)==0)
|
|
|
|
poselib_backup_restore(pld);
|
|
|
|
else
|
|
|
|
pld->flag &= ~PL_PREVIEW_FIRSTTIME;
|
|
|
|
|
|
|
|
/* pose should be the right one to draw (unless we're temporarily not showing it) */
|
2009-04-16 00:53:22 +00:00
|
|
|
if ((pld->flag & PL_PREVIEW_SHOWORIGINAL)==0) {
|
|
|
|
RNA_int_set(op->ptr, "pose_index", BLI_findindex(&pld->act->markers, pld->marker));
|
2009-04-15 12:04:10 +00:00
|
|
|
poselib_apply_pose(pld);
|
2009-04-16 00:53:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
RNA_int_set(op->ptr, "pose_index", -2); /* -2 means don't apply any pose */
|
2009-04-15 12:04:10 +00:00
|
|
|
|
|
|
|
/* old optimize trick... this enforces to bypass the depgraph
|
|
|
|
* - note: code copied from transform_generics.c -> recalcData()
|
|
|
|
*/
|
|
|
|
// FIXME: shouldn't this use the builtin stuff?
|
|
|
|
if ((pld->arm->flag & ARM_DELAYDEFORM)==0)
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&pld->ob->id, OB_RECALC_DATA); /* sets recalc flags */
|
2009-04-15 12:04:10 +00:00
|
|
|
else
|
|
|
|
where_is_pose(pld->scene, pld->ob);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do header print - if interactively previewing */
|
|
|
|
if (pld->state == PL_PREVIEW_RUNNING) {
|
|
|
|
if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
|
|
|
|
sprintf(pld->headerstr, "PoseLib Previewing Pose: [Showing Original Pose] | Use Tab to start previewing poses again");
|
|
|
|
ED_area_headerprint(pld->sa, pld->headerstr);
|
|
|
|
}
|
|
|
|
else if (pld->searchstr[0]) {
|
|
|
|
char tempstr[65];
|
|
|
|
char markern[64];
|
|
|
|
short index;
|
|
|
|
|
|
|
|
/* get search-string */
|
|
|
|
index= pld->search_cursor;
|
|
|
|
|
|
|
|
if (IN_RANGE(index, 0, 64)) {
|
|
|
|
memcpy(&tempstr[0], &pld->searchstr[0], index);
|
|
|
|
tempstr[index]= '|';
|
|
|
|
memcpy(&tempstr[index+1], &pld->searchstr[index], 64-index);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
2009-04-15 12:04:10 +00:00
|
|
|
else {
|
|
|
|
strncpy(tempstr, pld->searchstr, 64);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get marker name */
|
|
|
|
if (pld->marker)
|
|
|
|
strcpy(markern, pld->marker->name);
|
|
|
|
else
|
|
|
|
strcpy(markern, "No Matches");
|
|
|
|
|
|
|
|
sprintf(pld->headerstr, "PoseLib Previewing Pose: Filter - [%s] | Current Pose - \"%s\" | Use ScrollWheel or PageUp/Down to change", tempstr, markern);
|
|
|
|
ED_area_headerprint(pld->sa, pld->headerstr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sprintf(pld->headerstr, "PoseLib Previewing Pose: \"%s\" | Use ScrollWheel or PageUp/Down to change", pld->marker->name);
|
|
|
|
ED_area_headerprint(pld->sa, pld->headerstr);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
2009-04-15 12:04:10 +00:00
|
|
|
|
|
|
|
/* request drawing of view + clear redraw flag */
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM|ND_POSE, pld->ob);
|
|
|
|
pld->redraw= PL_PREVIEW_NOREDRAW;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------- */
|
|
|
|
|
|
|
|
/* This helper function is called during poselib_preview_poses to find the
|
|
|
|
* pose to preview next (after a change event)
|
|
|
|
*/
|
|
|
|
static void poselib_preview_get_next (tPoseLib_PreviewData *pld, int step)
|
|
|
|
{
|
|
|
|
/* check if we no longer have search-string, but don't have any marker */
|
|
|
|
if (pld->marker == NULL) {
|
|
|
|
if ((step) && (pld->searchstr[0] == 0))
|
|
|
|
pld->marker= pld->act->markers.first;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the following operations assume that there is a starting point and direction */
|
|
|
|
if ((pld->marker) && (step)) {
|
|
|
|
/* 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 (strcmp(pld->searchstr, pld->searchold)) {
|
|
|
|
/* free list of temporary search matches */
|
|
|
|
BLI_freelistN(&pld->searchp);
|
|
|
|
|
|
|
|
/* generate a new list of search matches */
|
|
|
|
for (marker= pld->act->markers.first; marker; marker= marker->next) {
|
|
|
|
/* does the name partially match?
|
|
|
|
* - 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 */
|
|
|
|
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) */
|
|
|
|
pld->marker= NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check if any matches */
|
|
|
|
if (pld->searchp.first == NULL) {
|
|
|
|
pld->marker= NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* find first match */
|
|
|
|
for (ldc= pld->searchp.first; ldc; ldc= ldc->next) {
|
|
|
|
if (ldc->data == pld->marker)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ldc == NULL)
|
|
|
|
ldc= pld->searchp.first;
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
for (ld=ldc; ld && step; ld=ldn, step--)
|
|
|
|
ldn= (ld->next) ? ld->next : pld->searchp.first;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (ld=ldc; ld && step; ld=ldn, step++)
|
|
|
|
ldn= (ld->prev) ? ld->prev : pld->searchp.last;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set marker */
|
|
|
|
if (ld)
|
|
|
|
pld->marker= ld->data;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
TimeMarker *marker, *next;
|
|
|
|
|
|
|
|
/* 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) {
|
|
|
|
for (marker=pld->marker; marker && step; marker=next, step--)
|
|
|
|
next= (marker->next) ? marker->next : pld->act->markers.first;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (marker=pld->marker; marker && step; marker=next, step++)
|
|
|
|
next= (marker->prev) ? marker->prev : pld->act->markers.last;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* it should be fairly impossible for marker to be NULL */
|
|
|
|
if (marker)
|
|
|
|
pld->marker= marker;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* specially handle events for searching */
|
|
|
|
static void poselib_preview_handle_search (tPoseLib_PreviewData *pld, unsigned short event, char ascii)
|
|
|
|
{
|
2009-04-15 12:04:10 +00:00
|
|
|
/* try doing some form of string manipulation first */
|
|
|
|
switch (event) {
|
|
|
|
case BACKSPACEKEY:
|
|
|
|
if (pld->searchstr[0] && pld->search_cursor) {
|
|
|
|
short len= strlen(pld->searchstr);
|
|
|
|
short index= pld->search_cursor;
|
|
|
|
short i;
|
|
|
|
|
|
|
|
for (i = index; i <= len; i++)
|
|
|
|
pld->searchstr[i-1] = pld->searchstr[i];
|
|
|
|
|
|
|
|
pld->search_cursor--;
|
|
|
|
|
|
|
|
poselib_preview_get_next(pld, 1);
|
|
|
|
pld->redraw = PL_PREVIEW_REDRAWALL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case DELKEY:
|
|
|
|
if (pld->searchstr[0] && pld->searchstr[1]) {
|
|
|
|
short len= strlen(pld->searchstr);
|
|
|
|
short index= pld->search_cursor;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (index < len) {
|
|
|
|
for (i = index; i < len; i++)
|
|
|
|
pld->searchstr[i] = pld->searchstr[i+1];
|
|
|
|
|
|
|
|
poselib_preview_get_next(pld, 1);
|
|
|
|
pld->redraw = PL_PREVIEW_REDRAWALL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
if (ascii) {
|
|
|
|
/* character to add to the string */
|
|
|
|
short index= pld->search_cursor;
|
|
|
|
short len= (pld->searchstr[0]) ? strlen(pld->searchstr) : 0;
|
|
|
|
short i;
|
|
|
|
|
|
|
|
if (len) {
|
|
|
|
for (i = len; i > index; i--)
|
|
|
|
pld->searchstr[i]= pld->searchstr[i-1];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pld->searchstr[1]= 0;
|
|
|
|
|
|
|
|
pld->searchstr[index]= ascii;
|
|
|
|
pld->search_cursor++;
|
|
|
|
|
|
|
|
poselib_preview_get_next(pld, 1);
|
|
|
|
pld->redraw = PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle events for poselib_preview_poses */
|
2009-04-15 12:38:04 +00:00
|
|
|
static int poselib_preview_handle_event (bContext *C, wmOperator *op, wmEvent *event)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 12:04:10 +00:00
|
|
|
tPoseLib_PreviewData *pld= op->customdata;
|
2009-04-15 12:38:04 +00:00
|
|
|
int ret = OPERATOR_RUNNING_MODAL;
|
2009-04-15 12:04:10 +00:00
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* backup stuff that needs to occur before every operation
|
|
|
|
* - make a copy of searchstr, so that we know if cache needs to be rebuilt
|
|
|
|
*/
|
|
|
|
strcpy(pld->searchold, pld->searchstr);
|
|
|
|
|
|
|
|
/* if we're currently showing the original pose, only certain events are handled */
|
|
|
|
if (pld->flag & PL_PREVIEW_SHOWORIGINAL) {
|
2009-04-15 12:04:10 +00:00
|
|
|
switch (event->type) {
|
2009-01-13 12:32:01 +00:00
|
|
|
/* exit - cancel */
|
|
|
|
case ESCKEY:
|
|
|
|
case RIGHTMOUSE:
|
|
|
|
pld->state= PL_PREVIEW_CANCEL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* exit - confirm */
|
|
|
|
case LEFTMOUSE:
|
|
|
|
case RETKEY:
|
|
|
|
case PADENTER:
|
|
|
|
case SPACEKEY:
|
|
|
|
pld->state= PL_PREVIEW_CONFIRM;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* view manipulation */
|
2009-04-15 12:38:04 +00:00
|
|
|
/* 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)
|
|
|
|
*/
|
2009-01-13 12:32:01 +00:00
|
|
|
case PAD0: case PAD1: case PAD2: case PAD3: case PAD4:
|
|
|
|
case PAD5: case PAD6: case PAD7: case PAD8: case PAD9:
|
2009-04-15 12:38:04 +00:00
|
|
|
case PADPLUSKEY: case PADMINUS: case MIDDLEMOUSE:
|
|
|
|
//pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
ret |= OPERATOR_PASS_THROUGH;
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
|
2009-04-15 12:38:04 +00:00
|
|
|
/* quicky compare to original */
|
2009-01-13 12:32:01 +00:00
|
|
|
case TABKEY:
|
2009-04-16 00:33:40 +00:00
|
|
|
/* only respond to one event */
|
|
|
|
if (event->val == 0) {
|
|
|
|
pld->flag &= ~PL_PREVIEW_SHOWORIGINAL;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EXITS HERE... */
|
2009-04-15 12:38:04 +00:00
|
|
|
return ret;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* NORMAL EVENT HANDLING... */
|
|
|
|
/* searching takes priority over normal activity */
|
2009-04-15 12:04:10 +00:00
|
|
|
switch (event->type) {
|
2009-01-13 12:32:01 +00:00
|
|
|
/* exit - cancel */
|
|
|
|
case ESCKEY:
|
|
|
|
case RIGHTMOUSE:
|
|
|
|
pld->state= PL_PREVIEW_CANCEL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* exit - confirm */
|
|
|
|
case LEFTMOUSE:
|
|
|
|
case RETKEY:
|
|
|
|
case PADENTER:
|
|
|
|
case SPACEKEY:
|
|
|
|
pld->state= PL_PREVIEW_CONFIRM;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* toggle between original pose and poselib pose*/
|
|
|
|
case TABKEY:
|
2009-04-16 00:33:40 +00:00
|
|
|
/* only respond to one event */
|
|
|
|
if (event->val == 0) {
|
|
|
|
pld->flag |= PL_PREVIEW_SHOWORIGINAL;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* change to previous pose (cyclic) */
|
|
|
|
case PAGEUPKEY:
|
|
|
|
case WHEELUPMOUSE:
|
|
|
|
poselib_preview_get_next(pld, -1);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* change to next pose (cyclic) */
|
|
|
|
case PAGEDOWNKEY:
|
|
|
|
case WHEELDOWNMOUSE:
|
|
|
|
poselib_preview_get_next(pld, 1);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* jump 5 poses (cyclic, back) */
|
|
|
|
case DOWNARROWKEY:
|
|
|
|
poselib_preview_get_next(pld, -5);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* jump 5 poses (cyclic, forward) */
|
|
|
|
case UPARROWKEY:
|
|
|
|
poselib_preview_get_next(pld, 5);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* change to next pose or searching cursor control */
|
|
|
|
case RIGHTARROWKEY:
|
|
|
|
if (pld->searchstr[0]) {
|
|
|
|
/* move text-cursor to the right */
|
|
|
|
if (pld->search_cursor < strlen(pld->searchstr))
|
|
|
|
pld->search_cursor++;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* change to next pose (cyclic) */
|
|
|
|
poselib_preview_get_next(pld, 1);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* change to next pose or searching cursor control */
|
|
|
|
case LEFTARROWKEY:
|
|
|
|
if (pld->searchstr[0]) {
|
|
|
|
/* move text-cursor to the left */
|
|
|
|
if (pld->search_cursor)
|
|
|
|
pld->search_cursor--;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* change to previous pose (cyclic) */
|
|
|
|
poselib_preview_get_next(pld, -1);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* change to first pose or start of searching string */
|
|
|
|
case HOMEKEY:
|
|
|
|
if (pld->searchstr[0]) {
|
|
|
|
pld->search_cursor= 0;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* change to first pose */
|
|
|
|
pld->marker= pld->act->markers.first;
|
|
|
|
pld->act->active_marker= 1;
|
|
|
|
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* change to last pose or start of searching string */
|
|
|
|
case ENDKEY:
|
|
|
|
if (pld->searchstr[0]) {
|
|
|
|
pld->search_cursor= strlen(pld->searchstr);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* change to last pose */
|
|
|
|
pld->marker= pld->act->markers.last;
|
|
|
|
pld->act->active_marker= BLI_countlist(&pld->act->markers);
|
|
|
|
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
}
|
|
|
|
break;
|
2009-04-15 12:38:04 +00:00
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* view manipulation */
|
2009-04-15 12:38:04 +00:00
|
|
|
/* 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)
|
|
|
|
*/
|
2009-01-13 12:32:01 +00:00
|
|
|
case MIDDLEMOUSE:
|
2009-04-15 12:38:04 +00:00
|
|
|
//pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
ret |= OPERATOR_PASS_THROUGH;
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* view manipulation, or searching */
|
|
|
|
case PAD0: case PAD1: case PAD2: case PAD3: case PAD4:
|
|
|
|
case PAD5: case PAD6: case PAD7: case PAD8: case PAD9:
|
|
|
|
case PADPLUSKEY: case PADMINUS:
|
|
|
|
if (pld->searchstr[0]) {
|
2009-04-15 12:38:04 +00:00
|
|
|
/* searching... */
|
|
|
|
poselib_preview_handle_search(pld, event->type, event->ascii);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
else {
|
2009-04-15 12:38:04 +00:00
|
|
|
/* view manipulation (see above) */
|
|
|
|
//pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
ret |= OPERATOR_PASS_THROUGH;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* otherwise, assume that searching might be able to handle it */
|
|
|
|
default:
|
2009-04-15 12:04:10 +00:00
|
|
|
poselib_preview_handle_search(pld, event->type, event->ascii);
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-04-15 12:38:04 +00:00
|
|
|
|
|
|
|
return ret;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------- */
|
|
|
|
|
|
|
|
/* Init PoseLib Previewing data */
|
2009-04-15 12:04:10 +00:00
|
|
|
static void poselib_preview_init_data (bContext *C, wmOperator *op)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 12:04:10 +00:00
|
|
|
tPoseLib_PreviewData *pld;
|
|
|
|
Object *ob= CTX_data_active_object(C);
|
2009-04-16 00:53:22 +00:00
|
|
|
int pose_index = RNA_int_get(op->ptr, "pose_index");
|
2009-04-15 12:04:10 +00:00
|
|
|
|
|
|
|
/* set up preview state info */
|
|
|
|
op->customdata= pld= MEM_callocN(sizeof(tPoseLib_PreviewData), "PoseLib Preview Data");
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* get basic data */
|
|
|
|
pld->ob= ob;
|
|
|
|
pld->arm= (ob) ? (ob->data) : NULL;
|
|
|
|
pld->pose= (ob) ? (ob->pose) : NULL;
|
|
|
|
pld->act= (ob) ? (ob->poselib) : NULL;
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
pld->scene= CTX_data_scene(C);
|
|
|
|
pld->sa= CTX_wm_area(C);
|
|
|
|
|
2009-04-16 00:53:22 +00:00
|
|
|
/* get starting pose based on RNA-props for this operator */
|
|
|
|
if (pose_index == -1)
|
|
|
|
pld->marker= poselib_get_active_pose(pld->act);
|
|
|
|
else if (pose_index == -2)
|
|
|
|
pld->flag |= PL_PREVIEW_SHOWORIGINAL;
|
|
|
|
else
|
|
|
|
pld->marker= (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL;
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* check if valid poselib */
|
|
|
|
if (ELEM3(NULL, pld->ob, pld->pose, pld->arm)) {
|
2009-04-15 12:04:10 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "PoseLib is only for Armatures in PoseMode");
|
2009-01-13 12:32:01 +00:00
|
|
|
pld->state= PL_PREVIEW_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pld->act == NULL) {
|
2009-04-15 12:04:10 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Object doesn't have a valid PoseLib");
|
2009-01-13 12:32:01 +00:00
|
|
|
pld->state= PL_PREVIEW_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pld->marker == NULL) {
|
2009-04-16 00:53:22 +00:00
|
|
|
if (pld->act->markers.first) {
|
2009-01-13 12:32:01 +00:00
|
|
|
/* just use first one then... */
|
|
|
|
pld->marker= pld->act->markers.first;
|
2009-04-16 00:53:22 +00:00
|
|
|
if (pose_index > -2) printf("PoseLib had no active pose\n");
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
else {
|
2009-04-15 12:04:10 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "PoseLib has no poses to preview/apply");
|
2009-01-13 12:32:01 +00:00
|
|
|
pld->state= PL_PREVIEW_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* get ID pointer for applying poses */
|
|
|
|
RNA_id_pointer_create(&ob->id, &pld->rna_ptr);
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* make backups for restoring pose */
|
|
|
|
poselib_backup_posecopy(pld);
|
|
|
|
|
|
|
|
/* set flags for running */
|
2009-04-16 00:53:22 +00:00
|
|
|
pld->state= PL_PREVIEW_RUNNING;
|
2009-01-13 12:32:01 +00:00
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
2009-04-16 00:53:22 +00:00
|
|
|
pld->flag |= PL_PREVIEW_FIRSTTIME;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* set depsgraph flags */
|
|
|
|
/* 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 */
|
|
|
|
strcpy(pld->headerstr, "");
|
|
|
|
strcpy(pld->searchstr, "");
|
|
|
|
strcpy(pld->searchold, "");
|
|
|
|
pld->search_cursor= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* After previewing poses */
|
2009-04-15 12:04:10 +00:00
|
|
|
static void poselib_preview_cleanup (bContext *C, wmOperator *op)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 12:04:10 +00:00
|
|
|
tPoseLib_PreviewData *pld= (tPoseLib_PreviewData *)op->customdata;
|
|
|
|
Scene *scene= pld->scene;
|
2009-01-13 12:32:01 +00:00
|
|
|
Object *ob= pld->ob;
|
|
|
|
bPose *pose= pld->pose;
|
|
|
|
bArmature *arm= pld->arm;
|
|
|
|
bAction *act= pld->act;
|
|
|
|
TimeMarker *marker= pld->marker;
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* redraw the header so that it doesn't show any of our stuff anymore */
|
|
|
|
ED_area_headerprint(pld->sa, NULL);
|
|
|
|
|
2009-01-13 12:32:01 +00:00
|
|
|
/* this signal does one recalc on pose, then unlocks, so ESC or edit will work */
|
|
|
|
pose->flag |= POSE_DO_UNLOCK;
|
|
|
|
|
|
|
|
/* clear pose if cancelled */
|
|
|
|
if (pld->state == PL_PREVIEW_CANCEL) {
|
|
|
|
poselib_backup_restore(pld);
|
|
|
|
|
|
|
|
/* old optimize trick... this enforces to bypass the depgraph
|
|
|
|
* - note: code copied from transform_generics.c -> recalcData()
|
|
|
|
*/
|
|
|
|
if ((arm->flag & ARM_DELAYDEFORM)==0)
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_DATA); /* sets recalc flags */
|
2009-01-13 12:32:01 +00:00
|
|
|
else
|
|
|
|
where_is_pose(scene, ob);
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (pld->state == PL_PREVIEW_CONFIRM) {
|
|
|
|
/* tag poses as appropriate */
|
2009-09-13 12:34:00 +00:00
|
|
|
poselib_keytag_pose(C, scene, pld);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* change active pose setting */
|
|
|
|
act->active_marker= BLI_findindex(&act->markers, marker) + 1;
|
|
|
|
action_set_activemarker(act, marker, 0);
|
|
|
|
|
|
|
|
/* Update event for pose and deformation children */
|
2.5
Notifiers
---------
Various fixes for wrong use of notifiers, and some new notifiers
to make things a bit more clear and consistent, with two notable
changes:
* Geometry changes are now done with NC_GEOM, rather than
NC_OBJECT|ND_GEOM_, so an object does need to be available.
* Space data now use NC_SPACE|ND_SPACE_*, instead of data
notifiers or even NC_WINDOW in some cases. Note that NC_SPACE
should only be used for notifying about changes in space data,
we don't want to go back to allqueue(REDRAW..).
Depsgraph
---------
The dependency graph now has a different flush call:
DAG_object_flush_update(scene, ob, flag)
is replaced by:
DAG_id_flush_update(id, flag)
It still works basically the same, one difference is that it now
also accepts object data (e.g. Mesh), again to avoid requiring an
Object to be available. Other ID types will simply do nothing at
the moment.
Docs
----
I made some guidelines for how/when to do which kinds of updates
and notifiers. I can't specify totally exact how to make these
decisions, but these are basically the guidelines I use. So, new
and updated docs are here:
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/NotifiersUpdates
http://wiki.blender.org/index.php/BlenderDev/Blender2.5/DataNotifiers
2009-09-04 20:51:09 +00:00
|
|
|
DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* updates */
|
2009-02-16 03:01:56 +00:00
|
|
|
if (IS_AUTOKEY_MODE(scene, NORMAL)) {
|
2009-04-15 12:04:10 +00:00
|
|
|
//remake_action_ipos(ob->action);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* need to trick depgraph, action is not allowed to execute on pose */
|
|
|
|
where_is_pose(scene, ob);
|
|
|
|
ob->recalc= 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free memory used for backups */
|
|
|
|
BLI_freelistN(&pld->backups);
|
|
|
|
BLI_freelistN(&pld->searchp);
|
2009-04-15 12:04:10 +00:00
|
|
|
|
|
|
|
/* free temp data for operator */
|
|
|
|
MEM_freeN(pld);
|
|
|
|
op->customdata= NULL;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* End previewing operation */
|
|
|
|
static int poselib_preview_exit (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
tPoseLib_PreviewData *pld= op->customdata;
|
|
|
|
|
|
|
|
/* finish up */
|
|
|
|
poselib_preview_cleanup(C, op);
|
|
|
|
|
|
|
|
if (ELEM(pld->state, PL_PREVIEW_CANCEL, PL_PREVIEW_ERROR))
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
else
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* Cancel previewing operation (called when exiting Blender) */
|
|
|
|
static int poselib_preview_cancel (bContext *C, wmOperator *op)
|
|
|
|
{
|
|
|
|
poselib_preview_exit(C, op);
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
}
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* main modal status check */
|
|
|
|
static int poselib_preview_modal (bContext *C, wmOperator *op, wmEvent *event)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 12:38:04 +00:00
|
|
|
tPoseLib_PreviewData *pld= op->customdata;
|
|
|
|
int ret;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* 1) check state to see if we're still running */
|
2009-04-16 00:53:22 +00:00
|
|
|
if (pld->state != PL_PREVIEW_RUNNING)
|
2009-04-15 12:04:10 +00:00
|
|
|
return poselib_preview_exit(C, op);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:38:04 +00:00
|
|
|
/* 2) handle events */
|
|
|
|
ret= poselib_preview_handle_event(C, op, event);
|
2009-04-15 12:04:10 +00:00
|
|
|
|
2009-04-15 12:38:04 +00:00
|
|
|
/* 3) apply changes and redraw, otherwise, confirming goes wrong */
|
|
|
|
if (pld->redraw)
|
|
|
|
poselib_preview_apply(C, op);
|
|
|
|
|
2009-04-16 00:53:22 +00:00
|
|
|
return ret;
|
2009-04-15 12:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Modal Operator init */
|
|
|
|
static int poselib_preview_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
|
|
|
{
|
|
|
|
tPoseLib_PreviewData *pld;
|
|
|
|
|
|
|
|
/* check if everything is ok, and init settings for modal operator */
|
|
|
|
poselib_preview_init_data(C, op);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-15 12:38:04 +00:00
|
|
|
/* do initial apply to have something to look at */
|
|
|
|
poselib_preview_apply(C, op);
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* add temp handler if we're running as a modal operator */
|
2009-09-18 12:43:36 +00:00
|
|
|
WM_event_add_modal_handler(C, op);
|
2009-04-15 12:04:10 +00:00
|
|
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
2009-04-16 00:33:40 +00:00
|
|
|
/* Repeat operator */
|
|
|
|
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);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-04-16 00:53:22 +00:00
|
|
|
/* 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;
|
|
|
|
|
2009-04-16 00:33:40 +00:00
|
|
|
/* apply the active pose */
|
|
|
|
poselib_preview_apply(C, op);
|
|
|
|
|
2009-04-16 00:53:22 +00:00
|
|
|
/* now, set the status to exit */
|
|
|
|
pld->state = PL_PREVIEW_CONFIRM;
|
|
|
|
|
2009-04-16 00:33:40 +00:00
|
|
|
/* cleanup */
|
|
|
|
return poselib_preview_exit(C, op);
|
|
|
|
}
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
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";
|
|
|
|
|
|
|
|
/* api callbacks */
|
|
|
|
ot->invoke= poselib_preview_invoke;
|
|
|
|
ot->modal= poselib_preview_modal;
|
|
|
|
ot->cancel= poselib_preview_cancel;
|
2009-04-16 00:33:40 +00:00
|
|
|
ot->exec= poselib_preview_exec;
|
2009-04-15 12:04:10 +00:00
|
|
|
ot->poll= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
2009-07-11 14:51:13 +00:00
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* properties */
|
2009-04-16 00:53:22 +00:00
|
|
|
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);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|