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"
|
|
|
|
|
|
|
|
#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_ipo_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"
|
|
|
|
|
|
|
|
#include "PIL_time.h" /* sleep */
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
ListBase keys = {NULL, NULL};
|
|
|
|
ActKeyColumn *ak;
|
|
|
|
TimeMarker *marker, *markern;
|
|
|
|
|
|
|
|
/* validate action and poselib */
|
|
|
|
if (act == NULL) {
|
|
|
|
error("No Action to validate");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* determine which frames have keys */
|
|
|
|
action_to_keylist(act, &keys, NULL, NULL);
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
BLI_freelistN(&keys);
|
|
|
|
|
|
|
|
BIF_undo_push("PoseLib Validate Action");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* Pointers to the builtin KeyingSets that we want to use */
|
|
|
|
static KeyingSet *poselib_ks_locrotscale = NULL; /* quaternion rotations */
|
|
|
|
static KeyingSet *poselib_ks_locrotscale2 = NULL; /* euler rotations */ // XXX FIXME...
|
|
|
|
static short poselib_ks_need_init= 1; /* have the above been obtained yet? */
|
|
|
|
|
|
|
|
/* Make sure the builtin KeyingSets are initialised properly
|
|
|
|
* (only gets called on first run of poselib_add_current_pose).
|
|
|
|
*/
|
|
|
|
static void poselib_get_builtin_keyingsets (void)
|
2009-01-13 12:32:01 +00:00
|
|
|
{
|
2009-04-15 01:10:36 +00:00
|
|
|
/* only if we haven't got these yet */
|
|
|
|
// FIXME: this assumes that we will always get the builtin sets...
|
|
|
|
if (poselib_ks_need_init) {
|
|
|
|
/* LocRotScale (quaternions) */
|
|
|
|
poselib_ks_locrotscale= ANIM_builtin_keyingset_get_named(NULL, "LocRotScale");
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* LocRotScale (euler) */
|
|
|
|
//ks_locrotscale2= ANIM_builtin_keyingset_get_named(ks_locrotscale, "LocRotScale");
|
|
|
|
poselib_ks_locrotscale2= poselib_ks_locrotscale; // FIXME: for now, just use the same one...
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* clear flag requesting init */
|
|
|
|
poselib_ks_need_init= 0;
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-15 03:22:22 +00:00
|
|
|
/* ----- */
|
|
|
|
|
|
|
|
static void poselib_add_menu_invoke__replacemenu (bContext *C, uiMenuItem *head, void *arg)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
uiMenuItemIntO(head, marker->name, ICON_ARMATURE_DATA, "POSELIB_OT_pose_add", "frame", marker->frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
uiMenuItem *head;
|
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (ELEM3(NULL, ob, arm, pose))
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
/* start building */
|
|
|
|
head= uiPupMenuBegin(op->type->name, 0);
|
|
|
|
uiMenuContext(head, WM_OP_EXEC_DEFAULT);
|
|
|
|
|
|
|
|
/* add new (adds to the first unoccupied frame) */
|
|
|
|
uiMenuItemIntO(head, "Add New", 0, "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) */
|
|
|
|
uiMenuItemIntO(head, "Add New (Current Frame)", 0, "POSELIB_OT_pose_add", "frame", CFRA);
|
|
|
|
|
|
|
|
/* replace existing - submenu */
|
|
|
|
uiMenuLevel(head, "Replace Existing...", poselib_add_menu_invoke__replacemenu);
|
|
|
|
}
|
|
|
|
|
|
|
|
uiPupMenuEnd(C, head);
|
|
|
|
|
|
|
|
/* 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-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 */
|
|
|
|
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
|
|
|
/* make sure we've got KeyingSets to use */
|
|
|
|
poselib_get_builtin_keyingsets();
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* turn on group-name overrides temporarily (only here) */
|
|
|
|
poselib_ks_locrotscale->flag |= KEYINGSET_GROUPNAMES_OVERRIDE;
|
|
|
|
poselib_ks_locrotscale2->flag |= KEYINGSET_GROUPNAMES_OVERRIDE;
|
|
|
|
|
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-04-15 01:10:36 +00:00
|
|
|
/* KeyingSet to use depends on rotation mode */
|
|
|
|
if (pchan->rotmode)
|
2009-04-15 03:22:22 +00:00
|
|
|
modify_keyframes(C, &dsources, act, poselib_ks_locrotscale2, MODIFYKEY_MODE_INSERT, (float)frame);
|
2009-04-15 01:10:36 +00:00
|
|
|
else
|
2009-04-15 03:22:22 +00:00
|
|
|
modify_keyframes(C, &dsources, act, poselib_ks_locrotscale, MODIFYKEY_MODE_INSERT, (float)frame);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* turn off group-name overrides so they don't affect the KeyingSets when used elsewhere */
|
|
|
|
poselib_ks_locrotscale->flag &= ~KEYINGSET_GROUPNAMES_OVERRIDE;
|
|
|
|
poselib_ks_locrotscale2->flag &= ~KEYINGSET_GROUPNAMES_OVERRIDE;
|
|
|
|
|
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;
|
|
|
|
uiMenuItem *head;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* sanity check */
|
|
|
|
if (ELEM(NULL, ob, act))
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
|
|
|
/* start building */
|
|
|
|
head= uiPupMenuBegin(op->type->name, 0);
|
|
|
|
uiMenuContext(head, WM_OP_EXEC_DEFAULT);
|
|
|
|
|
|
|
|
/* add each marker to this menu */
|
|
|
|
for (marker=act->markers.first, i=0; marker; marker= marker->next, i++)
|
|
|
|
uiMenuItemIntO(head, marker->name, ICON_ARMATURE_DATA, op->idname, "index", i);
|
|
|
|
|
|
|
|
uiPupMenuEnd(C, head);
|
|
|
|
|
|
|
|
/* 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-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-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-01-13 12:32:01 +00:00
|
|
|
BLI_uniquename(&act->markers, marker, "Pose", offsetof(TimeMarker, name), 64);
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
bActionChannel *achan;
|
|
|
|
bPoseChannel *pchan;
|
|
|
|
|
|
|
|
/* for each posechannel that has an actionchannel in */
|
|
|
|
for (achan= pld->act->chanbase.first; achan; achan= achan->next) {
|
|
|
|
/* try to find posechannel */
|
|
|
|
pchan= get_pose_channel(pld->pose, achan->name);
|
|
|
|
|
|
|
|
/* 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:04:10 +00:00
|
|
|
|
|
|
|
printf("apply pose ---> %d \n", frame);
|
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)) {
|
2009-04-15 12:04:10 +00:00
|
|
|
printf("\tact group %s ok \n", agrp->name);
|
|
|
|
|
2009-04-15 01:10:36 +00:00
|
|
|
/* 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 01:10:36 +00:00
|
|
|
if (ok) {
|
2009-04-15 12:04:10 +00:00
|
|
|
printf("\t\tevaluating... \n");
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-04-15 12:04:10 +00:00
|
|
|
else
|
|
|
|
printf("\tact group %s not ok \n", agrp->name);
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Auto-keys/tags bones affected by the pose used from the poselib */
|
|
|
|
static void poselib_keytag_pose (Scene *scene, tPoseLib_PreviewData *pld)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
|
|
/* 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) {
|
2.5: Blender "Animato" - New Animation System
Finally, here is the basic (functional) prototype of the new animation system which will allow for the infamous "everything is animatable", and which also addresses several of the more serious shortcomings of the old system. Unfortunately, this will break old animation files (especially right now, as I haven't written the version patching code yet), however, this is for the future.
Highlights of the new system:
* Scrapped IPO-Curves/IPO/(Action+Constraint-Channels)/Action system, and replaced it with F-Curve/Action.
- F-Curves (animators from other packages will feel at home with this name) replace IPO-Curves.
- The 'new' Actions, act as the containers for F-Curves, so that they can be reused. They are therefore more akin to the old 'IPO' blocks, except they do not have the blocktype restriction, so you can store materials/texture/geometry F-Curves in the same Action as Object transforms, etc.
* F-Curves use RNA-paths for Data Access, hence allowing "every" (where sensible/editable that is) user-accessible setting from RNA to be animated.
* Drivers are no longer mixed with Animation Data, so rigs will not be that easily broken and several dependency problems can be eliminated. (NOTE: drivers haven't been hooked up yet, but the code is in place)
* F-Curve modifier system allows useful 'large-scale' manipulation of F-Curve values, including (I've only included implemented ones here): envelope deform (similar to lattices to allow broad-scale reshaping of curves), curve generator (polynomial or py-expression), cycles (replacing the old cyclic extrapolation modes, giving more control over this). (NOTE: currently this cannot be tested, as there's not access to them, but the code is all in place)
* NLA system with 'tracks' (i.e. layers), and multiple strips per track. (NOTE: NLA system is not yet functional, as it's only partially coded still)
There are more nice things that I will be preparing some nice docs for soon, but for now, check for more details:
http://lists.blender.org/pipermail/bf-taskforce25/2009-January/000260.html
So, what currently works:
* I've implemented two basic operators for the 3D-view only to Insert and Delete Keyframes. These are tempolary ones only that will be replaced in due course with 'proper' code.
* Object Loc/Rot/Scale can be keyframed. Also, the colour of the 'active' material (Note: this should really be for nth material instead, but that doesn't work yet in RNA) can also be keyframed into the same datablock.
* Standard animation refresh (i.e. animation resulting from NLA and Action evaluation) is now done completely separate from drivers before anything else is done after a frame change. Drivers are handled after this in a separate pass, as dictated by depsgraph flags, etc.
Notes:
* Drivers haven't been hooked up yet
* Only objects and data directly linked to objects can be animated.
* Depsgraph will need further tweaks. Currently, I've only made sure that it will update some things in the most basic cases (i.e. frame change).
* Animation Editors are currently broken (in terms of editing stuff). This will be my next target (priority to get Dopesheet working first, then F-Curve editor - i.e. old IPO Editor)
* I've had to put in large chunks of XXX sandboxing for old animation system code all around the place. This will be cleaned up in due course, as some places need special review.
In particular, the particles and sequencer code have far too many manual calls to calculate + flush animation info, which is really bad (this is a 'please explain yourselves' call to Physics coders!).
2009-01-17 03:12:50 +00:00
|
|
|
#if 0 // XXX old animation system
|
2009-01-13 12:32:01 +00:00
|
|
|
// TODO: use a standard autokeying function in future (to allow autokeying-editkeys to work)
|
|
|
|
if (IS_AUTOKEY_MODE(NORMAL)) {
|
|
|
|
ID *id= &pld->ob->id;
|
|
|
|
|
|
|
|
/* Set keys on pose */
|
|
|
|
if (pchan->flag & POSE_ROT) {
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_X, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Z, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_W, 0);
|
|
|
|
}
|
|
|
|
if (pchan->flag & POSE_SIZE) {
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_X, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Y, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Z, 0);
|
|
|
|
}
|
|
|
|
if (pchan->flag & POSE_LOC) {
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_X, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Y, 0);
|
|
|
|
insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Z, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* clear any unkeyed tags */
|
|
|
|
if (pchan->bone)
|
|
|
|
pchan->bone->flag &= ~BONE_UNKEYED;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* add unkeyed tags */
|
|
|
|
if (pchan->bone)
|
|
|
|
pchan->bone->flag |= BONE_UNKEYED;
|
|
|
|
}
|
2.5: Blender "Animato" - New Animation System
Finally, here is the basic (functional) prototype of the new animation system which will allow for the infamous "everything is animatable", and which also addresses several of the more serious shortcomings of the old system. Unfortunately, this will break old animation files (especially right now, as I haven't written the version patching code yet), however, this is for the future.
Highlights of the new system:
* Scrapped IPO-Curves/IPO/(Action+Constraint-Channels)/Action system, and replaced it with F-Curve/Action.
- F-Curves (animators from other packages will feel at home with this name) replace IPO-Curves.
- The 'new' Actions, act as the containers for F-Curves, so that they can be reused. They are therefore more akin to the old 'IPO' blocks, except they do not have the blocktype restriction, so you can store materials/texture/geometry F-Curves in the same Action as Object transforms, etc.
* F-Curves use RNA-paths for Data Access, hence allowing "every" (where sensible/editable that is) user-accessible setting from RNA to be animated.
* Drivers are no longer mixed with Animation Data, so rigs will not be that easily broken and several dependency problems can be eliminated. (NOTE: drivers haven't been hooked up yet, but the code is in place)
* F-Curve modifier system allows useful 'large-scale' manipulation of F-Curve values, including (I've only included implemented ones here): envelope deform (similar to lattices to allow broad-scale reshaping of curves), curve generator (polynomial or py-expression), cycles (replacing the old cyclic extrapolation modes, giving more control over this). (NOTE: currently this cannot be tested, as there's not access to them, but the code is all in place)
* NLA system with 'tracks' (i.e. layers), and multiple strips per track. (NOTE: NLA system is not yet functional, as it's only partially coded still)
There are more nice things that I will be preparing some nice docs for soon, but for now, check for more details:
http://lists.blender.org/pipermail/bf-taskforce25/2009-January/000260.html
So, what currently works:
* I've implemented two basic operators for the 3D-view only to Insert and Delete Keyframes. These are tempolary ones only that will be replaced in due course with 'proper' code.
* Object Loc/Rot/Scale can be keyframed. Also, the colour of the 'active' material (Note: this should really be for nth material instead, but that doesn't work yet in RNA) can also be keyframed into the same datablock.
* Standard animation refresh (i.e. animation resulting from NLA and Action evaluation) is now done completely separate from drivers before anything else is done after a frame change. Drivers are handled after this in a separate pass, as dictated by depsgraph flags, etc.
Notes:
* Drivers haven't been hooked up yet
* Only objects and data directly linked to objects can be animated.
* Depsgraph will need further tweaks. Currently, I've only made sure that it will update some things in the most basic cases (i.e. frame change).
* Animation Editors are currently broken (in terms of editing stuff). This will be my next target (priority to get Dopesheet working first, then F-Curve editor - i.e. old IPO Editor)
* I've had to put in large chunks of XXX sandboxing for old animation system code all around the place. This will be cleaned up in due course, as some places need special review.
In particular, the particles and sequencer code have far too many manual calls to calculate + flush animation info, which is really bad (this is a 'please explain yourselves' call to Physics coders!).
2009-01-17 03:12:50 +00:00
|
|
|
#endif // XXX old animation system
|
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;
|
|
|
|
|
|
|
|
printf("do apply(C, op) \n");
|
|
|
|
|
|
|
|
/* only recalc pose (and its dependencies) if pose has changed */
|
|
|
|
if (pld->redraw == PL_PREVIEW_REDRAWALL) {
|
|
|
|
printf("\tupdate pose \n");
|
|
|
|
|
|
|
|
/* 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) */
|
|
|
|
if ((pld->flag & PL_PREVIEW_SHOWORIGINAL)==0)
|
|
|
|
poselib_apply_pose(pld);
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
DAG_object_flush_update(pld->scene, pld->ob, OB_RECALC_DATA); /* sets recalc flags */
|
|
|
|
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:04:10 +00:00
|
|
|
static void 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-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;
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
#if 0 // XXX these are for view manipulation... how to get it working?
|
2009-01-13 12:32:01 +00:00
|
|
|
/* view manipulation */
|
|
|
|
case MIDDLEMOUSE:
|
|
|
|
// there's a little bug here that causes the normal header to get drawn while view is manipulated
|
|
|
|
// XXX handle_view_middlemouse();
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
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:
|
|
|
|
//persptoetsen(event);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
break;
|
2009-04-15 12:04:10 +00:00
|
|
|
#endif // XXX these are for view manipulation... how to get it working?
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
case TABKEY:
|
|
|
|
pld->flag &= ~PL_PREVIEW_SHOWORIGINAL;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* EXITS HERE... */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2009-04-15 12:04:10 +00:00
|
|
|
printf("poselib event -> set confirm \n");
|
2009-01-13 12:32:01 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* toggle between original pose and poselib pose*/
|
|
|
|
case TABKEY:
|
|
|
|
pld->flag |= PL_PREVIEW_SHOWORIGINAL;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
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:04:10 +00:00
|
|
|
#if 0 // XXX these are for view manipulation... how to get it working?
|
2009-01-13 12:32:01 +00:00
|
|
|
/* view manipulation */
|
|
|
|
case MIDDLEMOUSE:
|
|
|
|
// there's a little bug here that causes the normal header to get drawn while view is manipulated
|
|
|
|
// XXX handle_view_middlemouse();
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
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]) {
|
|
|
|
poselib_preview_handle_search(pld, event, ascii);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
persptoetsen(event);
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWHEADER;
|
|
|
|
}
|
|
|
|
break;
|
2009-04-15 12:04:10 +00:00
|
|
|
#endif // XXX these are for view manipulation... how to get it working?
|
2009-01-13 12:32:01 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------- */
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
short apply_active= RNA_boolean_get(op->ptr, "apply_active");
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
pld->marker= poselib_get_active_pose(pld->act);
|
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
pld->scene= CTX_data_scene(C);
|
|
|
|
pld->sa= CTX_wm_area(C);
|
|
|
|
|
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) {
|
|
|
|
if ((apply_active==0) && (pld->act->markers.first)) {
|
|
|
|
/* just use first one then... */
|
|
|
|
pld->marker= pld->act->markers.first;
|
|
|
|
printf("PoseLib had no active pose\n");
|
|
|
|
}
|
|
|
|
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 */
|
|
|
|
pld->state= (apply_active) ? PL_PREVIEW_RUNONCE : PL_PREVIEW_RUNNING;
|
|
|
|
pld->redraw= PL_PREVIEW_REDRAWALL;
|
|
|
|
pld->flag= PL_PREVIEW_FIRSTTIME;
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
DAG_object_flush_update(scene, ob, OB_RECALC_DATA); /* sets recalc flags */
|
|
|
|
else
|
|
|
|
where_is_pose(scene, ob);
|
|
|
|
|
|
|
|
}
|
|
|
|
else if (pld->state == PL_PREVIEW_CONFIRM) {
|
|
|
|
/* tag poses as appropriate */
|
|
|
|
poselib_keytag_pose(scene, pld);
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
|
|
|
|
|
|
|
|
/* 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:04:10 +00:00
|
|
|
tPoseLib_PreviewData *pld= op->customdata;
|
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 */
|
|
|
|
if (ELEM(pld->state, PL_PREVIEW_RUNONCE, PL_PREVIEW_RUNNING) == 0)
|
|
|
|
return poselib_preview_exit(C, op);
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* 2) apply changes and redraw */
|
|
|
|
if (pld->redraw)
|
|
|
|
poselib_preview_apply(C, op);
|
|
|
|
|
|
|
|
/* check if time to exit */
|
|
|
|
if (pld->state == PL_PREVIEW_RUNONCE) {
|
|
|
|
/* set status to cleanup time */
|
|
|
|
pld->state = PL_PREVIEW_CONFIRM;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
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:04:10 +00:00
|
|
|
/* 3) handle events */
|
|
|
|
poselib_preview_handle_event(C, op, event);
|
|
|
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add temp handler if we're running as a modal operator */
|
|
|
|
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
|
|
|
|
|
|
|
|
return OPERATOR_RUNNING_MODAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
//ot->exec= poselib_preview_exec;
|
|
|
|
ot->poll= ED_operator_posemode;
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
2009-01-13 12:32:01 +00:00
|
|
|
|
2009-04-15 12:04:10 +00:00
|
|
|
/* properties */
|
|
|
|
// FIXME: the following should be removed...
|
|
|
|
RNA_def_boolean(ot->srna, "apply_active", 0, "Apply Active Pose", "Simply apply the active pose and exit");
|
2009-01-13 12:32:01 +00:00
|
|
|
}
|