diff options
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/SConscript | 3 | ||||
-rw-r--r-- | source/blender/editors/animation/Makefile | 54 | ||||
-rw-r--r-- | source/blender/editors/animation/SConscript | 9 | ||||
-rw-r--r-- | source/blender/editors/animation/anim_draw.c | 174 | ||||
-rw-r--r-- | source/blender/editors/animation/anim_filter.c | 208 | ||||
-rw-r--r-- | source/blender/editors/animation/anim_keyframing.c | 2286 | ||||
-rw-r--r-- | source/blender/editors/animation/anim_markers.c (renamed from source/blender/editors/util/ed_markers.c) | 2 | ||||
-rw-r--r-- | source/blender/editors/include/ED_anim_api.h | 172 | ||||
-rw-r--r-- | source/blender/editors/include/ED_keyframing.h | 134 | ||||
-rw-r--r-- | source/blender/editors/space_action/SConscript | 2 | ||||
-rw-r--r-- | source/blender/editors/space_action/space_action.c | 12 | ||||
-rw-r--r-- | source/blender/editors/space_time/space_time.c | 3 | ||||
-rw-r--r-- | source/blender/editors/space_time/time_ops.c | 10 |
13 files changed, 3059 insertions, 10 deletions
diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript index 84afc5512a7..e890ac69d76 100644 --- a/source/blender/editors/SConscript +++ b/source/blender/editors/SConscript @@ -6,6 +6,7 @@ SConscript(['datafiles/SConscript', 'space_api/SConscript', 'util/SConscript', 'interface/SConscript', + 'animation/SConscript', 'mesh/SConscript', 'object/SConscript', 'space_buttons/SConscript', @@ -23,5 +24,5 @@ SConscript(['datafiles/SConscript', 'space_script/SConscript', 'space_text/SConscript', 'space_sequencer/SConscript', - 'transform/SConscript', + 'transform/SConscript', # XXX this should be moved near start of list, as many modules depend on this? 'screen/SConscript']) diff --git a/source/blender/editors/animation/Makefile b/source/blender/editors/animation/Makefile new file mode 100644 index 00000000000..19b62891b63 --- /dev/null +++ b/source/blender/editors/animation/Makefile @@ -0,0 +1,54 @@ +# +# $Id: Makefile 14 2002-10-13 15:57:19Z hans $ +# +# ***** 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 +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +# ***** END GPL LICENSE BLOCK ***** +# +# Makes module object directory and bounces make to subdirectories. + +LIBNAME = ed_animation +DIR = $(OCGDIR)/blender/$(LIBNAME) + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) + +CPPFLAGS += -I$(NAN_GLEW)/include +CPPFLAGS += -I$(OPENGL_HEADERS) + +# not very neat.... +CPPFLAGS += -I../../windowmanager +CPPFLAGS += -I../../blenloader +CPPFLAGS += -I../../blenkernel +CPPFLAGS += -I../../blenlib +CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../makesrna +CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../python +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include + +# own include + +CPPFLAGS += -I../include diff --git a/source/blender/editors/animation/SConscript b/source/blender/editors/animation/SConscript new file mode 100644 index 00000000000..b972cdf2256 --- /dev/null +++ b/source/blender/editors/animation/SConscript @@ -0,0 +1,9 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') + +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../makesrna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' + +env.BlenderLib ( 'bf_editors_animation', sources, Split(incs), [], libtype=['core','intern'], priority=[35, 40] ) diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c new file mode 100644 index 00000000000..e2bc73847c0 --- /dev/null +++ b/source/blender/editors/animation/anim_draw.c @@ -0,0 +1,174 @@ +/** + * $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) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> +#include <stdio.h> + +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_util.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +/* *************************************************** */ +/* CURRENT FRAME DRAWING */ + +/* Draw current frame number in a little green box beside the current frame indicator */ +static void draw_cfra_number (View2D *v2d, float cfra, short time) +{ + float xscale, yscale, yspace, ypixels, x; + short slen; + char str[32]; + + /* because the frame number text is subject to the same scaling as the contents of the view */ + UI_view2d_getscale(v2d, &xscale, &yscale); + glScalef(1.0/xscale, 1.0, 1.0); + + if (time) + sprintf(str, " %.2f", FRA2TIME(CFRA)); + else + sprintf(str, " %d", CFRA); + slen= UI_GetStringWidth(G.font, str, 0); + + /* get starting coordinates for drawing */ + x= cfra * xscale; + + /* draw green box around/behind text */ + UI_ThemeColor(TH_CFRAME); + UI_ThemeColorShadeAlpha(TH_CFRAME, 0, -100); + glRectf(x, 0, x+slen, 15); + + /* draw current frame number - black text */ + UI_ThemeColor(TH_TEXT); + ui_rasterpos_safe(x-5, 17, 1.0); + UI_DrawString(G.fonts, str, 0); + + /* restore view transform */ + glScalef(xscale, yscale, 1.0); +} + +/* General call for drawing current frame indicator in a */ +void ANIM_draw_cfra (const bContext *C, View2D *v2d, short flag) +{ + Scene *scene= CTX_data_scene(C); + float vec[2]; + + /* Draw a light green line to indicate current frame */ + vec[0]= (float)(scene->r.cfra * scene->r.framelen); + + UI_ThemeColor(TH_CFRAME); + glLineWidth(2.0); + + glBegin(GL_LINE_STRIP); + vec[1]= v2d->cur.ymin; + glVertex2fv(vec); + + vec[1]= v2d->cur.ymax; + glVertex2fv(vec); + glEnd(); + + /* Draw dark green line if slow-parenting/time-offset is enabled */ + if (flag & DRAWCFRA_SHOW_TIMEOFS) { + Object *ob= (scene->basact) ? (scene->basact->object) : 0; + if ((ob) && (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)!=0.0)) { + vec[0]-= give_timeoffset(ob); /* could avoid calling twice */ + + UI_ThemeColorShade(TH_CFRAME, -30); + + glBegin(GL_LINE_STRIP); + /*vec[1]= v2d->cur.ymax;*/ // this is set already. this line is only included + glVertex2fv(vec); + + vec[1]= v2d->cur.ymin; + glVertex2fv(vec); + glEnd(); + } + } + + glLineWidth(1.0); + + /* Draw current frame number in a little box */ + if (flag & DRAWCFRA_SHOW_NUMBOX) { + UI_view2d_view_orthoSpecial(C, v2d, 1); + draw_cfra_number(v2d, vec[0], (flag & DRAWCFRA_UNIT_SECONDS)); + } +} + +/* *************************************************** */ +/* PREVIEW RANGE 'CURTAINS' */ + +/* Draw preview range 'curtains' for highlighting where the animation data is */ +void ANIM_draw_previewrange (const bContext *C, View2D *v2d) +{ + Scene *scene= CTX_data_scene(C); + + /* only draw this if preview range is set */ + if (scene->r.psfra) { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + glColor4f(0.0f, 0.0f, 0.0f, 0.4f); + + /* only draw two separate 'curtains' if there's no overlap between them */ + if (PSFRA < PEFRA) { + glRectf(v2d->cur.xmin, v2d->cur.ymin, PSFRA, v2d->cur.ymax); + glRectf(PEFRA, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + } + else { + glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + } + + glDisable(GL_BLEND); + } +} + +/* *************************************************** */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c new file mode 100644 index 00000000000..e82e61aa158 --- /dev/null +++ b/source/blender/editors/animation/anim_filter.c @@ -0,0 +1,208 @@ +/** + * $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) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* This file defines the system for filtering data into a form suitable for + * use by the Animation Editors, thus acting as a means by which the Animation + * Editors maintain a level of abstraction from the data they actually manipulate. + * Thus, they only need to check on the type of the data they're manipulating, and + * NOT worry about various layers of context/hierarchy checks. + * + * While this is primarily used for the Action/Dopesheet Editor (and its accessory modes), + * the IPO Editor also uses this for it's channel list and for determining which curves + * are being edited. + * + * -- Joshua Leung, Dec 2008 + */ + +#include <string.h> +#include <stdio.h> + +#include "DNA_listbase.h" +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_lattice_types.h" +#include "DNA_key_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_screen.h" +#include "BKE_utildefines.h" + +#include "ED_anim_api.h" +#include "ED_types.h" +#include "ED_util.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +/* ************************************************************ */ +/* Blender Context <-> Animation Context mapping */ + +/* ----------- Private Stuff - Action Editor ------------- */ + +/* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */ +/* Note: there's a similar function in key.c (ob_get_key) */ +Key *actedit_get_shapekeys (const bContext *C, SpaceAction *saction) +{ + Scene *scene= CTX_data_scene(C); + Object *ob; + Key *key; + + ob = OBACT; // XXX er... + if (ob == NULL) + return NULL; + + /* pinning is not available in 'ShapeKey' mode... */ + //if (saction->pin) return NULL; + + /* shapekey data is stored with geometry data */ + switch (ob->type) { + case OB_MESH: + key= ((Mesh *)ob->data)->key; + break; + + case OB_LATTICE: + key= ((Lattice *)ob->data)->key; + break; + + case OB_CURVE: + case OB_SURF: + key= ((Curve *)ob->data)->key; + break; + + default: + return NULL; + } + + if (key) { + if (key->type == KEY_RELATIVE) + return key; + } + + return NULL; +} + +/* Get data being edited in Action Editor (depending on current 'mode') */ +static void *actedit_get_context (const bContext *C, SpaceAction *saction, short *datatype) +{ + Scene *scene= CTX_data_scene(C); + + /* sync settings with current view status, then return appropriate data */ + switch (saction->mode) { + case SACTCONT_ACTION: /* 'Action Editor' */ + /* if not pinned, sync with active object */ + if (saction->pin == 0) { + if (OBACT) + saction->action = OBACT->action; + else + saction->action= NULL; + } + + *datatype= ANIMCONT_ACTION; + return saction->action; + + case SACTCONT_SHAPEKEY: /* 'ShapeKey Editor' */ + *datatype= ANIMCONT_SHAPEKEY; + return actedit_get_shapekeys(C, saction); + + case SACTCONT_GPENCIL: /* Grease Pencil */ // XXX review how this mode is handled... + *datatype=ANIMCONT_GPENCIL; + return CTX_wm_screen(C); // FIXME: add that dopesheet type thing here! + break; + + case SACTCONT_DOPESHEET: /* DopeSheet */ + /* update scene-pointer (no need to check for pinning yet, as not implemented) */ + saction->ads.source= (ID *)scene; + + *datatype= ANIMCONT_DOPESHEET; + return &saction->ads; + + default: /* unhandled yet */ + *datatype= ANIMCONT_NONE; + return NULL; + } +} + +/* ----------- Private Stuff - IPO Editor ------------- */ + +/* ----------- Public API --------------- */ + +/* Obtain current anim-data context from Blender Context info */ +void *animdata_get_context (const bContext *C, short *datatype) +{ + ScrArea *sa= CTX_wm_area(C); + + /* set datatype to 'None' for convenience */ + if (datatype == NULL) return NULL; + *datatype= ANIMCONT_NONE; + if (sa == NULL) return NULL; /* highly unlikely to happen, but still! */ + + /* context depends on editor we are currently in */ + switch (sa->spacetype) { + case SPACE_ACTION: + { + SpaceAction *saction= (SpaceAction *)CTX_wm_space_data(C); + return actedit_get_context(C, saction, datatype); + } + break; + + case SPACE_IPO: + { + SpaceIpo *sipo= (SpaceIpo *)CTX_wm_space_data(C); + // ... + } + break; + } + + /* nothing appropriate */ + return NULL; +} + +/* ************************************************************ */ + +/* ************************************************************ */ diff --git a/source/blender/editors/animation/anim_keyframing.c b/source/blender/editors/animation/anim_keyframing.c new file mode 100644 index 00000000000..3e303a2d26c --- /dev/null +++ b/source/blender/editors/animation/anim_keyframing.c @@ -0,0 +1,2286 @@ +/** + * $Id: keyframing.c 17745 2008-12-08 09:16:09Z aligorith $ + * + * ***** 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) 2008, Blender Foundation + * This is a new part of Blender (with some old code) + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#if 0 // XXX reenable this file again later... + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> +#include <float.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_dynstr.h" + +#include "DNA_listBase.h" +#include "DNA_ID.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_constraint_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_object_types.h" +#include "DNA_material_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_texture_types.h" +#include "DNA_userdef_types.h" +#include "DNA_vec_types.h" +#include "DNA_view3d_types.h" +#include "DNA_world_types.h" + +#include "BKE_global.h" +#include "BKE_utildefines.h" +#include "BKE_blender.h" +#include "BKE_main.h" // XXX not needed old cruft? +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_constraint.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_object.h" +#include "BKE_material.h" + +#include "ED_keyframing.h" + +#if 0 // XXX resolve these old dependencies! +#include "BIF_butspace.h" +#include "BIF_editaction.h" +#include "BIF_editkey.h" +#include "BIF_interface.h" +#include "BIF_mywindow.h" +#include "BIF_poseobject.h" +#include "BIF_screen.h" +#include "BIF_space.h" +#include "BIF_toolbox.h" +#include "BIF_toets.h" + +#include "BSE_editipo.h" +#include "BSE_node.h" +#include "BSE_time.h" +#include "BSE_view.h" + +#include "blendef.h" + +#include "PIL_time.h" /* sleep */ +#include "mydevice.h" +#endif // XXX resolve these old dependencies! + + + +/* ************************************************** */ +/* LOCAL TYPES AND DEFINES */ + +/* ----------- Common KeyData Sources ------------ */ + +/* temporary struct to gather data combos to keyframe */ +typedef struct bCommonKeySrc { + struct bCommonKeySrc *next, *prev; + + /* general data/destination-source settings */ + ID *id; /* id-block this comes from */ + char *actname; /* name of action channel */ + char *constname; /* name of constraint channel */ + + /* general destination source settings */ + Ipo *ipo; /* ipo-block that id-block has (optional) */ + bAction *act; /* action-block that id-block has (optional) */ + + /* pose-level settings */ + bPoseChannel *pchan; /* pose channel */ + + /* buttons-window settings */ + int map; /* offset to apply to certain adrcodes */ +} bCommonKeySrc; + +/* -------------- Keying Sets ------------------- */ + +/* storage for iterator for looping over keyingset channels */ +typedef struct bKS_AdrcodeGetter { + struct bKeyingSet *ks; /* keyingset this applies to */ + struct bCommonKeySrc *cks; /* data to insert/delete keyframes... */ + + short index; /* index of current channel to resume from */ + short tot; /* index after which we start returning from some special collection */ +} bKS_AdrcodeGetter; + +/* flags to look out for in keyingset channels... */ +#define KAG_CHAN_EXTEND (-1) + + +/* keying set - a set of channels that will be keyframed together */ +// TODO: move this to a header to allow custom sets someday? +typedef struct bKeyingSet { + /* callback func to consider if keyingset should be included + * (by default, if this is undefined, item will be shown) + */ + short (*include_cb)(struct bKeyingSet *, const char *); + + char name[48]; /* name of keyingset */ + int blocktype; /* blocktype that all channels belong to */ // in future, this may be eliminated + short flag; /* flags to use when setting keyframes */ + + short chan_num; /* number of channels to insert keyframe in */ + short adrcodes[32]; /* adrcodes for channels to insert keys for (ideally would be variable-len, but limit of 32 will suffice) */ +} bKeyingSet; + +/* keying set context - an array of keying sets and the number of them */ +typedef struct bKeyingContext { + bKeyingSet *keyingsets; /* array containing the keyingsets of interest */ + bKeyingSet *lastused; /* item that was chosen last time*/ + int tot; /* number of keyingsets in */ +} bKeyingContext; + +/* ************************************************** */ +/* KEYFRAME INSERTION */ + +/* -------------- BezTriple Insertion -------------------- */ + +/* threshold for inserting keyframes - threshold here should be good enough for now, but should become userpref */ +#define BEZT_INSERT_THRESH 0.00001 + +/* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_icu) + * Returns the index to insert at (data already at that index will be offset if replace is 0) + */ +static int binarysearch_bezt_index (BezTriple array[], float frame, int arraylen, short *replace) +{ + int start=0, end=arraylen; + int loopbreaker= 0, maxloop= arraylen * 2; + + /* initialise replace-flag first */ + *replace= 0; + + /* sneaky optimisations (don't go through searching process if...): + * - keyframe to be added is to be added out of current bounds + * - keyframe to be added would replace one of the existing ones on bounds + */ + if ((arraylen <= 0) || (array == NULL)) { + printf("Warning: binarysearch_bezt_index() encountered invalid array \n"); + return 0; + } + else { + /* check whether to add before/after/on */ + float framenum; + + /* 'First' Keyframe (when only one keyframe, this case is used) */ + framenum= array[0].vec[1][0]; + if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { + *replace = 1; + return 0; + } + else if (frame < framenum) + return 0; + + /* 'Last' Keyframe */ + framenum= array[(arraylen-1)].vec[1][0]; + if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) { + *replace= 1; + return (arraylen - 1); + } + else if (frame > framenum) + return arraylen; + } + + + /* most of the time, this loop is just to find where to put it + * 'loopbreaker' is just here to prevent infinite loops + */ + for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) { + /* compute and get midpoint */ + int mid = (start + end) / 2; + float midfra= array[mid].vec[1][0]; + + /* check if exactly equal to midpoint */ + if (IS_EQT(frame, midfra, BEZT_INSERT_THRESH)) { + *replace = 1; + return mid; + } + + /* repeat in upper/lower half */ + if (frame > midfra) + start= mid + 1; + else if (frame < midfra) + end= mid - 1; + } + + /* print error if loop-limit exceeded */ + if (loopbreaker == (maxloop-1)) { + printf("Error: binarysearch_bezt_index() was taking too long \n"); + + // include debug info + printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen); + } + + /* not found, so return where to place it */ + return start; +} + +/* This function adds a given BezTriple to an IPO-Curve. It will allocate + * memory for the array if needed, and will insert the BezTriple into a + * suitable place in chronological order. + * + * NOTE: any recalculate of the IPO-Curve that needs to be done will need to + * be done by the caller. + */ +int insert_bezt_icu (IpoCurve *icu, BezTriple *bezt) +{ + BezTriple *newb; + int i= 0; + + if (icu->bezt) { + short replace = -1; + i = binarysearch_bezt_index(icu->bezt, bezt->vec[1][0], icu->totvert, &replace); + + if (replace) { + /* sanity check: 'i' may in rare cases exceed arraylen */ + // FIXME: do not overwrite handletype if just replacing...? + if ((i >= 0) && (i < icu->totvert)) + *(icu->bezt + i) = *bezt; + } + else { + /* add new */ + newb= MEM_callocN((icu->totvert+1)*sizeof(BezTriple), "beztriple"); + + /* add the beztriples that should occur before the beztriple to be pasted (originally in ei->icu) */ + if (i > 0) + memcpy(newb, icu->bezt, i*sizeof(BezTriple)); + + /* add beztriple to paste at index i */ + *(newb + i)= *bezt; + + /* add the beztriples that occur after the beztriple to be pasted (originally in icu) */ + if (i < icu->totvert) + memcpy(newb+i+1, icu->bezt+i, (icu->totvert-i)*sizeof(BezTriple)); + + /* replace (+ free) old with new */ + MEM_freeN(icu->bezt); + icu->bezt= newb; + + icu->totvert++; + } + } + else { + icu->bezt= MEM_callocN(sizeof(BezTriple), "beztriple"); + *(icu->bezt)= *bezt; + icu->totvert= 1; + } + + + /* we need to return the index, so that some tools which do post-processing can + * detect where we added the BezTriple in the array + */ + return i; +} + +/* This function is a wrapper for insert_bezt_icu, and should be used when + * adding a new keyframe to a curve, when the keyframe doesn't exist anywhere + * else yet. + * + * 'fast' - is only for the python API where importing BVH's would take an extreamly long time. + */ +void insert_vert_icu (IpoCurve *icu, float x, float y, short fast) +{ + BezTriple beztr; + int a, h1, h2; + + /* set all three points, for nicer start position */ + memset(&beztr, 0, sizeof(BezTriple)); + beztr.vec[0][0]= x; + beztr.vec[0][1]= y; + beztr.vec[1][0]= x; + beztr.vec[1][1]= y; + beztr.vec[2][0]= x; + beztr.vec[2][1]= y; + beztr.hide= IPO_BEZ; + beztr.f1= beztr.f2= beztr.f3= SELECT; + beztr.h1= beztr.h2= HD_AUTO; + + /* add temp beztriple to keyframes */ + a= insert_bezt_icu(icu, &beztr); + + /* what if 'a' is a negative index? + * for now, just exit to prevent any segfaults + */ + if (a < 0) return; + + /* don't recalculate handles if fast is set + * - this is a hack to make importers faster + * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) + */ + if (!fast) calchandles_ipocurve(icu); + + /* set handletype and interpolation */ + if (icu->totvert > 2) { + BezTriple *bezt= (icu->bezt + a); + + /* set handles (autohandles by default) */ + h1= h2= HD_AUTO; + + if (a > 0) h1= (bezt-1)->h2; + if (a < icu->totvert-1) h2= (bezt+1)->h1; + + bezt->h1= h1; + bezt->h2= h2; + + /* set interpolation (if curve is using IPO_MIXED, then take from previous) */ + if (icu->ipo == IPO_MIXED) { + if (a > 0) bezt->ipo= (bezt-1)->ipo; + else if (a < icu->totvert-1) bezt->ipo= (bezt+1)->ipo; + } + else + bezt->ipo= icu->ipo; + + /* don't recalculate handles if fast is set + * - this is a hack to make importers faster + * - we may calculate twice (see editipo_changed(), due to autohandle needing two calculations) + */ + if (!fast) calchandles_ipocurve(icu); + } + else { + BezTriple *bezt= (icu->bezt + a); + + /* set interpolation directly from ipo-curve */ + bezt->ipo= icu->ipo; + } +} + +/* ------------------- Get Data ------------------------ */ + +/* Get pointer to use to get values from */ +// FIXME: this should not be possible with Data-API +static void *get_context_ipo_poin (ID *id, int blocktype, char *actname, char *constname, IpoCurve *icu, int *vartype) +{ + switch (blocktype) { + case ID_PO: /* posechannel */ + if (GS(id->name)==ID_OB) { + Object *ob= (Object *)id; + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + + if (pchan) { + if (ELEM3(icu->adrcode, AC_EUL_X, AC_EUL_Y, AC_EUL_Z)) + *vartype= IPO_FLOAT_DEGR; + else + *vartype= IPO_FLOAT; + return get_pchan_ipo_poin(pchan, icu->adrcode); + } + } + break; + + case ID_CO: /* constraint */ + if ((GS(id->name)==ID_OB) && (constname && constname[0])) { + Object *ob= (Object *)id; + bConstraint *con; + + /* assume that we only want the influence (as only used for Constraint Channels) */ + if ((ob->ipoflag & OB_ACTION_OB) && !strcmp(actname, "Object")) { + for (con= ob->constraints.first; con; con= con->next) { + if (strcmp(constname, con->name)==0) { + *vartype= IPO_FLOAT; + return &con->enforce; + } + } + } + else if (ob->pose) { + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + + if (pchan) { + for (con= pchan->constraints.first; con; con= con->next) { + if (strcmp(constname, con->name)==0) { + *vartype= IPO_FLOAT; + return &con->enforce; + } + } + } + } + } + break; + + case ID_OB: /* object */ + /* hack: layer channels for object need to be keyed WITHOUT localview flag... + * tsk... tsk... why must we just dump bitflags upon users :/ + */ + if ((GS(id->name)==ID_OB) && (icu->adrcode==OB_LAY)) { + Object *ob= (Object *)id; + static int layer = 0; + + /* init layer to be the object's layer var, then remove local view from it */ + layer = ob->lay; + layer &= 0xFFFFFF; + *vartype= IPO_INT_BIT; + + /* return pointer to this static var + * - assumes that this pointer won't be stored for use later, so may not be threadsafe + * if multiple keyframe calls are made, but that is unlikely to happen in the near future + */ + return (void *)(&layer); + } + /* no break here for other ob channel-types - as they can be done normally */ + + default: /* normal data-source */ + return get_ipo_poin(id, icu, vartype); + } + + /* not valid... */ + return NULL; +} + + +/* -------------- 'Smarter' Keyframing Functions -------------------- */ +/* return codes for new_key_needed */ +enum { + KEYNEEDED_DONTADD = 0, + KEYNEEDED_JUSTADD, + KEYNEEDED_DELPREV, + KEYNEEDED_DELNEXT +} eKeyNeededStatus; + +/* This helper function determines whether a new keyframe is needed */ +/* Cases where keyframes should not be added: + * 1. Keyframe to be added bewteen two keyframes with similar values + * 2. Keyframe to be added on frame where two keyframes are already situated + * 3. Keyframe lies at point that intersects the linear line between two keyframes + */ +static short new_key_needed (IpoCurve *icu, float cFrame, float nValue) +{ + BezTriple *bezt=NULL, *prev=NULL; + int totCount, i; + float valA = 0.0f, valB = 0.0f; + + /* safety checking */ + if (icu == NULL) return KEYNEEDED_JUSTADD; + totCount= icu->totvert; + if (totCount == 0) return KEYNEEDED_JUSTADD; + + /* loop through checking if any are the same */ + bezt= icu->bezt; + for (i=0; i<totCount; i++) { + float prevPosi=0.0f, prevVal=0.0f; + float beztPosi=0.0f, beztVal=0.0f; + + /* get current time+value */ + beztPosi= bezt->vec[1][0]; + beztVal= bezt->vec[1][1]; + + if (prev) { + /* there is a keyframe before the one currently being examined */ + + /* get previous time+value */ + prevPosi= prev->vec[1][0]; + prevVal= prev->vec[1][1]; + + /* keyframe to be added at point where there are already two similar points? */ + if (IS_EQ(prevPosi, cFrame) && IS_EQ(beztPosi, cFrame) && IS_EQ(beztPosi, prevPosi)) { + return KEYNEEDED_DONTADD; + } + + /* keyframe between prev+current points ? */ + if ((prevPosi <= cFrame) && (cFrame <= beztPosi)) { + /* is the value of keyframe to be added the same as keyframes on either side ? */ + if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) { + return KEYNEEDED_DONTADD; + } + else { + float realVal; + + /* get real value of curve at that point */ + realVal= eval_icu(icu, cFrame); + + /* compare whether it's the same as proposed */ + if (IS_EQ(realVal, nValue)) + return KEYNEEDED_DONTADD; + else + return KEYNEEDED_JUSTADD; + } + } + + /* new keyframe before prev beztriple? */ + if (cFrame < prevPosi) { + /* A new keyframe will be added. However, whether the previous beztriple + * stays around or not depends on whether the values of previous/current + * beztriples and new keyframe are the same. + */ + if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) + return KEYNEEDED_DELNEXT; + else + return KEYNEEDED_JUSTADD; + } + } + else { + /* just add a keyframe if there's only one keyframe + * and the new one occurs before the exisiting one does. + */ + if ((cFrame < beztPosi) && (totCount==1)) + return KEYNEEDED_JUSTADD; + } + + /* continue. frame to do not yet passed (or other conditions not met) */ + if (i < (totCount-1)) { + prev= bezt; + bezt++; + } + else + break; + } + + /* Frame in which to add a new-keyframe occurs after all other keys + * -> If there are at least two existing keyframes, then if the values of the + * last two keyframes and the new-keyframe match, the last existing keyframe + * gets deleted as it is no longer required. + * -> Otherwise, a keyframe is just added. 1.0 is added so that fake-2nd-to-last + * keyframe is not equal to last keyframe. + */ + bezt= (icu->bezt + (icu->totvert - 1)); + valA= bezt->vec[1][1]; + + if (prev) + valB= prev->vec[1][1]; + else + valB= bezt->vec[1][1] + 1.0f; + + if (IS_EQ(valA, nValue) && IS_EQ(valA, valB)) + return KEYNEEDED_DELPREV; + else + return KEYNEEDED_JUSTADD; +} + +/* ------------------ 'Visual' Keyframing Functions ------------------ */ + +/* internal status codes for visualkey_can_use */ +enum { + VISUALKEY_NONE = 0, + VISUALKEY_LOC, + VISUALKEY_ROT +}; + +/* This helper function determines if visual-keyframing should be used when + * inserting keyframes for the given channel. As visual-keyframing only works + * on Object and Pose-Channel blocks, this should only get called for those + * blocktypes, when using "standard" keying but 'Visual Keying' option in Auto-Keying + * settings is on. + */ +static short visualkey_can_use (ID *id, int blocktype, char *actname, char *constname, int adrcode) +{ + Object *ob= NULL; + bConstraint *con= NULL; + short searchtype= VISUALKEY_NONE; + + /* validate data */ + if ((id == NULL) || (GS(id->name)!=ID_OB) || !(ELEM(blocktype, ID_OB, ID_PO))) + return 0; + + /* get first constraint and determine type of keyframe constraints to check for*/ + ob= (Object *)id; + + if (blocktype == ID_OB) { + con= ob->constraints.first; + + if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) + searchtype= VISUALKEY_LOC; + else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) + searchtype= VISUALKEY_ROT; + } + else if (blocktype == ID_PO) { + bPoseChannel *pchan= get_pose_channel(ob->pose, actname); + con= pchan->constraints.first; + + if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) + searchtype= VISUALKEY_LOC; + else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) + searchtype= VISUALKEY_ROT; + } + + /* only search if a searchtype and initial constraint are available */ + if (searchtype && con) { + for (; con; con= con->next) { + /* only consider constraint if it is not disabled, and has influence */ + if (con->flag & CONSTRAINT_DISABLE) continue; + if (con->enforce == 0.0f) continue; + + /* some constraints may alter these transforms */ + switch (con->type) { + /* multi-transform constraints */ + case CONSTRAINT_TYPE_CHILDOF: + return 1; + case CONSTRAINT_TYPE_TRANSFORM: + return 1; + case CONSTRAINT_TYPE_FOLLOWPATH: + return 1; + case CONSTRAINT_TYPE_KINEMATIC: + return 1; + + /* single-transform constraits */ + case CONSTRAINT_TYPE_TRACKTO: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_ROTLIMIT: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_LOCLIMIT: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_ROTLIKE: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_DISTLIMIT: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_LOCLIKE: + if (searchtype==VISUALKEY_LOC) return 1; + break; + case CONSTRAINT_TYPE_LOCKTRACK: + if (searchtype==VISUALKEY_ROT) return 1; + break; + case CONSTRAINT_TYPE_MINMAX: + if (searchtype==VISUALKEY_LOC) return 1; + break; + + default: + break; + } + } + } + + /* when some condition is met, this function returns, so here it can be 0 */ + return 0; +} + +/* This helper function extracts the value to use for visual-keyframing + * In the event that it is not possible to perform visual keying, try to fall-back + * to using the poin method. Assumes that all data it has been passed is valid. + */ +static float visualkey_get_value (ID *id, int blocktype, char *actname, char *constname, int adrcode, IpoCurve *icu) +{ + Object *ob; + void *poin = NULL; + int index, vartype; + + /* validate situtation */ + if ((id==NULL) || (GS(id->name)!=ID_OB) || (ELEM(blocktype, ID_OB, ID_PO)==0)) + return 0.0f; + + /* get object */ + ob= (Object *)id; + + /* only valid for objects or posechannels */ + if (blocktype == ID_OB) { + /* parented objects are not supported, as the effects of the parent + * are included in the matrix, which kindof beats the point + */ + if (ob->parent == NULL) { + /* only Location or Rotation keyframes are supported now */ + if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) { + /* assumes that OB_LOC_Z > OB_LOC_Y > OB_LOC_X */ + index= adrcode - OB_LOC_X; + + return ob->obmat[3][index]; + } + else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) { + float eul[3]; + + /* assumes that OB_ROT_Z > OB_ROT_Y > OB_ROT_X */ + index= adrcode - OB_ROT_X; + + Mat4ToEul(ob->obmat, eul); + return eul[index]*(5.72958f); + } + } + } + else if (blocktype == ID_PO) { + bPoseChannel *pchan; + float tmat[4][4]; + + /* get data to use */ + pchan= get_pose_channel(ob->pose, actname); + + /* Although it is not strictly required for this particular space conversion, + * arg1 must not be null, as there is a null check for the other conversions to + * be safe. Therefore, the active object is passed here, and in many cases, this + * will be what owns the pose-channel that is getting this anyway. + */ + Mat4CpyMat4(tmat, pchan->pose_mat); + constraint_mat_convertspace(ob, pchan, tmat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL); + + /* Loc, Rot/Quat keyframes are supported... */ + if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) { + /* assumes that AC_LOC_Z > AC_LOC_Y > AC_LOC_X */ + index= adrcode - AC_LOC_X; + + /* only use for non-connected bones */ + if ((pchan->bone->parent) && !(pchan->bone->flag & BONE_CONNECTED)) + return tmat[3][index]; + else if (pchan->bone->parent == NULL) + return tmat[3][index]; + } + else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) { + float trimat[3][3], quat[4]; + + /* assumes that AC_QUAT_Z > AC_QUAT_Y > AC_QUAT_X > AC_QUAT_W */ + index= adrcode - AC_QUAT_W; + + Mat3CpyMat4(trimat, tmat); + Mat3ToQuat_is_ok(trimat, quat); + + return quat[index]; + } + } + + /* as the function hasn't returned yet, try reading from poin */ + poin= get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); + if (poin) + return read_ipo_poin(poin, vartype); + else + return 0.0; +} + + +/* ------------------------- Insert Key API ------------------------- */ + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will insert a keyframe using the current value being keyframed. + * + * The flag argument is used for special settings that alter the behaviour of + * the keyframe insertion. These include the 'visual' keyframing modes, quick refresh, + * and extra keyframe filtering. + */ +short insertkey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) +{ + IpoCurve *icu; + + /* get ipo-curve */ + //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 1); // XXX this call needs to be in blenkernel + + /* only continue if we have an ipo-curve to add keyframe to */ + if (icu) { + float cfra = frame_to_float(CFRA); + float curval= 0.0f; + + /* apply special time tweaking */ + if (GS(id->name) == ID_OB) { + Object *ob= (Object *)id; + + /* apply NLA-scaling (if applicable) */ + if (actname && actname[0]) + cfra= get_action_frame(ob, cfra); + + /* ancient time-offset cruft */ + if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) { + /* actually frametofloat calc again! */ + cfra-= give_timeoffset(ob)*G.scene->r.framelen; + } + } + + /* obtain value to give keyframe */ + if ( (flag & INSERTKEY_MATRIX) && + (visualkey_can_use(id, blocktype, actname, constname, adrcode)) ) + { + /* visual-keying is only available for object and pchan datablocks, as + * it works by keyframing using a value extracted from the final matrix + * instead of using the kt system to extract a value. + */ + curval= visualkey_get_value(id, blocktype, actname, constname, adrcode, icu); + } + else { + void *poin; + int vartype; + + /* get pointer to data to read from */ + poin = get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype); + if (poin == NULL) { + printf("Insert Key: No pointer to variable obtained \n"); + return 0; + } + + /* use kt's read_poin function to extract value (kt->read_poin should + * exist in all cases, but it never hurts to check) + */ + curval= read_ipo_poin(poin, vartype); + } + + /* only insert keyframes where they are needed */ + if (flag & INSERTKEY_NEEDED) { + short insert_mode; + + /* check whether this curve really needs a new keyframe */ + insert_mode= new_key_needed(icu, cfra, curval); + + /* insert new keyframe at current frame */ + if (insert_mode) + insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* delete keyframe immediately before/after newly added */ + switch (insert_mode) { + case KEYNEEDED_DELPREV: + delete_icu_key(icu, icu->totvert-2, 1); + break; + case KEYNEEDED_DELNEXT: + delete_icu_key(icu, 1, 1); + break; + } + + /* only return success if keyframe added */ + if (insert_mode) + return 1; + } + else { + /* just insert keyframe */ + insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST)); + + /* return success */ + return 1; + } + } + + /* return failure */ + return 0; +} + + +/* ************************************************** */ +/* KEYFRAME DELETION */ + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will delete a keyframe at the current frame. + * + * The flag argument is used for special settings that alter the behaviour of + * the keyframe deletion. These include the quick refresh options. + */ +short deletekey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag) +{ + Ipo *ipo; + IpoCurve *icu; + + /* get ipo-curve + * Note: here is one of the places where we don't want new ipo + ipo-curve added! + * so 'add' var must be 0 + */ + // XXX funcs here need to be recoded in blenkernel... + //ipo= verify_ipo(id, blocktype, actname, constname, NULL, 0); + //icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 0); + + /* only continue if we have an ipo-curve to remove keyframes from */ + if (icu) { + float cfra = frame_to_float(CFRA); + short found = -1; + int i; + + /* apply special time tweaking */ + if (GS(id->name) == ID_OB) { + Object *ob= (Object *)id; + + /* apply NLA-scaling (if applicable) */ + if (actname && actname[0]) + cfra= get_action_frame(ob, cfra); + + /* ancient time-offset cruft */ + if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) { + /* actually frametofloat calc again! */ + cfra-= give_timeoffset(ob)*G.scene->r.framelen; + } + } + + /* try to find index of beztriple to get rid of */ + i = binarysearch_bezt_index(icu->bezt, cfra, icu->totvert, &found); + if (found) { + /* delete the key at the index (will sanity check + do recalc afterwards ) */ + delete_icu_key(icu, i, 1); + + /* Only delete curve too if there isn't an ipo-driver still hanging around on an empty curve */ + if (icu->totvert==0 && icu->driver==NULL) { + BLI_remlink(&ipo->curve, icu); + free_ipo_curve(icu); + } + + /* return success */ + return 1; + } + } + + /* return failure */ + return 0; +} + +/* ************************************************** */ +/* COMMON KEYFRAME MANAGEMENT (common_insertkey/deletekey) */ + +/* mode for common_modifykey */ +enum { + COMMONKEY_MODE_INSERT = 0, + COMMONKEY_MODE_DELETE, +} eCommonModifyKey_Modes; + +/* --------- KeyingSet Adrcode Getters ------------ */ + +/* initialise a channel-getter storage */ +static void ks_adrcodegetter_init (bKS_AdrcodeGetter *kag, bKeyingSet *ks, bCommonKeySrc *cks) +{ + /* error checking */ + if (kag == NULL) + return; + + if (ELEM(NULL, ks, cks)) { + /* set invalid settings that won't cause harm */ + kag->ks= NULL; + kag->cks= NULL; + kag->index= -2; + kag->tot= 0; + } + else { + /* store settings */ + kag->ks= ks; + kag->cks= cks; + + /* - index is -1, as that allows iterators to return first element + * - tot is chan_num by default, but may get overriden if -1 is encountered (for extension-type getters) + */ + kag->index= -1; + kag->tot= ks->chan_num; + } +} + +/* 'default' channel-getter that will be used when iterating through keyingset's channels + * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape + */ +static short ks_getnextadrcode_default (bKS_AdrcodeGetter *kag) +{ + bKeyingSet *ks= (kag)? kag->ks : NULL; + + /* error checking */ + if (ELEM(NULL, kag, ks)) return 0; + if (kag->tot <= 0) return 0; + + kag->index++; + if ((kag->index < 0) || (kag->index >= kag->tot)) return 0; + + /* return the adrcode stored at index then */ + return ks->adrcodes[kag->index]; +} + +/* add map flag (for MTex channels, as certain ones need special offset) */ +static short ks_getnextadrcode_addmap (bKS_AdrcodeGetter *kag) +{ + short adrcode= ks_getnextadrcode_default(kag); + + /* if there was an adrcode returned, assume that kag stuff is set ok */ + if (adrcode) { + bCommonKeySrc *cks= kag->cks; + bKeyingSet *ks= kag->ks; + + if (ELEM3(ks->blocktype, ID_MA, ID_LA, ID_WO)) { + switch (adrcode) { + case MAP_OFS_X: case MAP_OFS_Y: case MAP_OFS_Z: + case MAP_SIZE_X: case MAP_SIZE_Y: case MAP_SIZE_Z: + case MAP_R: case MAP_G: case MAP_B: case MAP_DVAR: + case MAP_COLF: case MAP_NORF: case MAP_VARF: case MAP_DISP: + adrcode += cks->map; + break; + } + } + } + + /* adrcode must be returned! */ + return adrcode; +} + +/* extend posechannel keyingsets with rotation info (when KAG_CHAN_EXTEND is encountered) + * - iteration will stop when adrcode <= 0 is encountered, so we use that as escape + * - when we encounter KAG_CHAN_EXTEND as adrcode, start returning our own + */ +static short ks_getnextadrcode_pchanrot (bKS_AdrcodeGetter *kag) +{ + /* hardcoded adrcode channels used here only + * - length is keyed-channels + 1 (last item must be 0 to escape) + */ + static short quat_adrcodes[5] = {AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z, 0}; + static short eul_adrcodes[4] = {AC_EUL_X, AC_EUL_Y, AC_EUL_Z, 0}; + + /* useful variables */ + bKeyingSet *ks= (kag)? kag->ks : NULL; + bCommonKeySrc *cks= (kag) ? kag->cks : NULL; + short index, adrcode; + + /* error checking */ + if (ELEM3(NULL, kag, ks, cks)) return 0; + if (ks->chan_num <= 0) return 0; + + /* get index + * - if past the last item (kag->tot), return stuff from our static arrays + * - otherwise, just keep returning stuff from the keyingset (but check out for -1!) + */ + kag->index++; + if (kag->index < 0) + return 0; + + /* normal (static stuff) */ + if (kag->index < kag->tot) { + /* get adrcode, and return if not KAG_CHAN_EXTEND (i.e. point for start of iteration) */ + adrcode= ks->adrcodes[kag->index]; + + if (adrcode != KAG_CHAN_EXTEND) + return adrcode; + else + kag->tot= kag->index; + } + + /* based on current rotation-mode + * - index can be at most 5, if we are to prevent segfaults + */ + index= kag->index - kag->tot; + if ((index < 0) || (index > 5)) + return 0; + + if (cks->pchan && cks->pchan->rotmode) + return eul_adrcodes[index]; + else + return quat_adrcodes[index]; +} + +/* ------------- KeyingSet Defines ------------ */ +/* Note: these must all be named with the defks_* prefix, otherwise the template macro will not work! */ + +/* macro for defining keyingset contexts */ +#define KSC_TEMPLATE(ctx_name) {&defks_##ctx_name[0], NULL, sizeof(defks_##ctx_name)/sizeof(bKeyingSet)} + +/* --- */ + +/* check if option not available for deleting keys */ +static short incl_non_del_keys (bKeyingSet *ks, const char mode[]) +{ + /* as optimisation, assume that it is sufficient to check only first letter + * of mode (int comparison should be faster than string!) + */ + //if (strcmp(mode, "Delete")==0) + if (mode && mode[0]=='D') + return 0; + + return 1; +} + +/* Object KeyingSets ------ */ + +/* check if include shapekey entry */ +static short incl_v3d_ob_shapekey (bKeyingSet *ks, const char mode[]) +{ + Object *ob= (G.obedit)? (G.obedit) : (OBACT); + char *newname= NULL; + + if(ob==NULL) + return 0; + + /* not available for delete mode */ + if (strcmp(mode, "Delete")==0) + return 0; + + /* check if is geom object that can get shapekeys */ + switch (ob->type) { + /* geometry? */ + case OB_MESH: newname= "Mesh"; break; + case OB_CURVE: newname= "Curve"; break; + case OB_SURF: newname= "Surface"; break; + case OB_LATTICE: newname= "Lattice"; break; + + /* not geometry! */ + default: + return 0; + } + + /* if ks is shapekey entry (this could be callled for separator before too!) */ + if (ks->flag == -3) + sprintf(ks->name, newname); + + /* if it gets here, it's ok */ + return 1; +} + +/* array for object keyingset defines */ +bKeyingSet defks_v3d_object[] = +{ + /* include_cb, adrcode-getter, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Loc", ID_OB, 0, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, + {NULL, "Rot", ID_OB, 0, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + {NULL, "Scale", ID_OB, 0, 3, {OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "LocRot", ID_OB, 0, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {NULL, "LocScale", ID_OB, 0, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "LocRotScale", ID_OB, 0, 9, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {NULL, "RotScale", ID_OB, 0, 6, + {OB_ROT_X,OB_ROT_Y,OB_ROT_Z, + OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}}, + + {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator + + {incl_non_del_keys, "VisualLoc", ID_OB, INSERTKEY_MATRIX, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}}, + {incl_non_del_keys, "VisualRot", ID_OB, INSERTKEY_MATRIX, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {incl_non_del_keys, "VisualLocRot", ID_OB, INSERTKEY_MATRIX, 6, + {OB_LOC_X,OB_LOC_Y,OB_LOC_Z, + OB_ROT_X,OB_ROT_Y,OB_ROT_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Layer", ID_OB, 0, 1, {OB_LAY}}, // icky option... + {NULL, "Available", ID_OB, -2, 0, {0}}, + + {incl_v3d_ob_shapekey, "%l%l", 0, -1, 0, {0}}, // separator (linked to shapekey entry) + {incl_v3d_ob_shapekey, "<ShapeKey>", ID_OB, -3, 0, {0}} +}; + +/* PoseChannel KeyingSets ------ */ + +/* array for posechannel keyingset defines */ +bKeyingSet defks_v3d_pchan[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Loc", ID_PO, 0, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, + {NULL, "Rot", ID_PO, COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, + {NULL, "Scale", ID_PO, 0, 3, {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "LocRot", ID_PO, COMMONKEY_PCHANROT, 4, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, + KAG_CHAN_EXTEND}}, + + {NULL, "LocScale", ID_PO, 0, 6, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, + AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}}, + + {NULL, "LocRotScale", ID_PO, COMMONKEY_PCHANROT, 7, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, + KAG_CHAN_EXTEND}}, + + {NULL, "RotScale", ID_PO, 0, 4, + {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z, + KAG_CHAN_EXTEND}}, + + {incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator + + {incl_non_del_keys, "VisualLoc", ID_PO, INSERTKEY_MATRIX, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}}, + {incl_non_del_keys, "VisualRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 1, {KAG_CHAN_EXTEND}}, + + {incl_non_del_keys, "VisualLocRot", ID_PO, INSERTKEY_MATRIX|COMMONKEY_PCHANROT, 4, + {AC_LOC_X,AC_LOC_Y,AC_LOC_Z, KAG_CHAN_EXTEND}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_PO, -2, 0, {0}} +}; + +/* Material KeyingSets ------ */ + +/* array for material keyingset defines */ +bKeyingSet defks_buts_shading_mat[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "RGB", ID_MA, 0, 3, {MA_COL_R,MA_COL_G,MA_COL_B}}, + {NULL, "Alpha", ID_MA, 0, 1, {MA_ALPHA}}, + {NULL, "Halo Size", ID_MA, 0, 1, {MA_HASIZE}}, + {NULL, "Mode", ID_MA, 0, 1, {MA_MODE}}, // evil bitflags + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "All Color", ID_MA, 0, 18, + {MA_COL_R,MA_COL_G,MA_COL_B, + MA_ALPHA,MA_HASIZE, MA_MODE, + MA_SPEC_R,MA_SPEC_G,MA_SPEC_B, + MA_REF,MA_EMIT,MA_AMB,MA_SPEC,MA_HARD, + MA_MODE,MA_TRANSLU,MA_ADD}}, + + {NULL, "All Mirror", ID_MA, 0, 5, + {MA_RAYM,MA_FRESMIR,MA_FRESMIRI, + MA_FRESTRA,MA_FRESTRAI}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_MA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_MA, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_MA, -2, 0, {0}} +}; + +/* World KeyingSets ------ */ + +/* array for world keyingset defines */ +bKeyingSet defks_buts_shading_wo[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Zenith RGB", ID_WO, 0, 3, {WO_ZEN_R,WO_ZEN_G,WO_ZEN_B}}, + {NULL, "Horizon RGB", ID_WO, 0, 3, {WO_HOR_R,WO_HOR_G,WO_HOR_B}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Mist", ID_WO, 0, 4, {WO_MISI,WO_MISTDI,WO_MISTSTA,WO_MISTHI}}, + {NULL, "Stars", ID_WO, 0, 5, {WO_STAR_R,WO_STAR_G,WO_STAR_B,WO_STARDIST,WO_STARSIZE}}, + + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_WO, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_WO, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_WO, -2, 0, {0}} +}; + +/* Lamp KeyingSets ------ */ + +/* array for lamp keyingset defines */ +bKeyingSet defks_buts_shading_la[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "RGB", ID_LA, 0, 3, {LA_COL_R,LA_COL_G,LA_COL_B}}, + {NULL, "Energy", ID_LA, 0, 1, {LA_ENERGY}}, + {NULL, "Spot Size", ID_LA, 0, 1, {LA_SPOTSI}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Ofs", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}}, + {NULL, "Size", ID_LA, COMMONKEY_ADDMAP, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}}, + + {NULL, "All Mapping", ID_LA, COMMONKEY_ADDMAP, 14, + {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z, + MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z, + MAP_R,MAP_G,MAP_B,MAP_DVAR, + MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_LA, -2, 0, {0}} +}; + +/* Texture KeyingSets ------ */ + +/* array for texture keyingset defines */ +bKeyingSet defks_buts_shading_tex[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Clouds", ID_TE, 0, 5, + {TE_NSIZE,TE_NDEPTH,TE_NTYPE, + TE_MG_TYP,TE_N_BAS1}}, + + {NULL, "Marble", ID_TE, 0, 7, + {TE_NSIZE,TE_NDEPTH,TE_NTYPE, + TE_TURB,TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, + + {NULL, "Stucci", ID_TE, 0, 5, + {TE_NSIZE,TE_NTYPE,TE_TURB, + TE_MG_TYP,TE_N_BAS1}}, + + {NULL, "Wood", ID_TE, 0, 6, + {TE_NSIZE,TE_NTYPE,TE_TURB, + TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}}, + + {NULL, "Magic", ID_TE, 0, 2, {TE_NDEPTH,TE_TURB}}, + + {NULL, "Blend", ID_TE, 0, 1, {TE_MG_TYP}}, + + {NULL, "Musgrave", ID_TE, 0, 6, + {TE_MG_TYP,TE_MGH,TE_MG_LAC, + TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN}}, + + {NULL, "Voronoi", ID_TE, 0, 9, + {TE_VNW1,TE_VNW2,TE_VNW3,TE_VNW4, + TE_VNMEXP,TE_VN_DISTM,TE_VN_COLT, + TE_ISCA,TE_NSIZE}}, + + {NULL, "Distorted Noise", ID_TE, 0, 4, + {TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN,TE_DISTA}}, + + {NULL, "Color Filter", ID_TE, 0, 5, + {TE_COL_R,TE_COL_G,TE_COL_B,TE_BRIGHT,TE_CONTRA}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_TE, -2, 0, {0}} +}; + +/* Object Buttons KeyingSets ------ */ + +/* check if include particles entry */ +static short incl_buts_ob (bKeyingSet *ks, const char mode[]) +{ + Object *ob= OBACT; + /* only if object is mesh type */ + + if(ob==NULL) return 0; + return (ob->type == OB_MESH); +} + +/* array for texture keyingset defines */ +bKeyingSet defks_buts_object[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {incl_buts_ob, "Surface Damping", ID_OB, 0, 1, {OB_PD_SDAMP}}, + {incl_buts_ob, "Random Damping", ID_OB, 0, 1, {OB_PD_RDAMP}}, + {incl_buts_ob, "Permeability", ID_OB, 0, 1, {OB_PD_PERM}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Force Strength", ID_OB, 0, 1, {OB_PD_FSTR}}, + {NULL, "Force Falloff", ID_OB, 0, 1, {OB_PD_FFALL}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_OB, -2, 0, {0}} // this will include ob-transforms too! +}; + +/* Camera Buttons KeyingSets ------ */ + +/* check if include internal-renderer entry */ +static short incl_buts_cam1 (bKeyingSet *ks, const char mode[]) +{ + /* only if renderer is internal renderer */ + return (G.scene->r.renderer==R_INTERN); +} + +/* check if include external-renderer entry */ +static short incl_buts_cam2 (bKeyingSet *ks, const char mode[]) +{ + /* only if renderer is internal renderer */ + return (G.scene->r.renderer!=R_INTERN); +} + +/* array for camera keyingset defines */ +bKeyingSet defks_buts_cam[] = +{ + /* include_cb, name, blocktype, flag, chan_num, adrcodes */ + {NULL, "Lens", ID_CA, 0, 1, {CAM_LENS}}, + {NULL, "Clipping", ID_CA, 0, 2, {CAM_STA,CAM_END}}, + {NULL, "Focal Distance", ID_CA, 0, 1, {CAM_YF_FDIST}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + + {incl_buts_cam2, "Aperture", ID_CA, 0, 1, {CAM_YF_APERT}}, + {incl_buts_cam1, "Viewplane Shift", ID_CA, 0, 2, {CAM_SHIFT_X,CAM_SHIFT_Y}}, + + {NULL, "%l", 0, -1, 0, {0}}, // separator + + {NULL, "Available", ID_CA, -2, 0, {0}} +}; + +/* --- */ + +/* Keying Context Defines - Must keep in sync with enumeration (eKS_Contexts) */ +bKeyingContext ks_contexts[] = +{ + KSC_TEMPLATE(v3d_object), + KSC_TEMPLATE(v3d_pchan), + + KSC_TEMPLATE(buts_shading_mat), + KSC_TEMPLATE(buts_shading_wo), + KSC_TEMPLATE(buts_shading_la), + KSC_TEMPLATE(buts_shading_tex), + + KSC_TEMPLATE(buts_object), + KSC_TEMPLATE(buts_cam) +}; + +/* Keying Context Enumeration - Must keep in sync with definitions*/ +typedef enum eKS_Contexts { + KSC_V3D_OBJECT = 0, + KSC_V3D_PCHAN, + + KSC_BUTS_MAT, + KSC_BUTS_WO, + KSC_BUTS_LA, + KSC_BUTS_TEX, + + KSC_BUTS_OB, + KSC_BUTS_CAM, + + /* make sure this last one remains untouched! */ + KSC_TOT_TYPES +} eKS_Contexts; + + +/* ---------------- KeyingSet Tools ------------------- */ + +/* helper for commonkey_context_get() - get keyingsets for 3d-view */ +static void commonkey_context_getv3d (ListBase *sources, bKeyingContext **ksc) +{ + Object *ob; + IpoCurve *icu; + + if ((OBACT) && (OBACT->flag & OB_POSEMODE)) { + bPoseChannel *pchan; + + /* pose-level */ + ob= OBACT; + *ksc= &ks_contexts[KSC_V3D_PCHAN]; + set_pose_keys(ob); /* sets pchan->flag to POSE_KEY if bone selected, and clears if not */ + + /* loop through posechannels */ + for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) { + if (pchan->flag & POSE_KEY) { + bCommonKeySrc *cks; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to, and action */ + cks->id= (ID *)ob; + cks->act= ob->action; + + /* set pchan */ + cks->pchan= pchan; + cks->actname= pchan->name; + } + } + } + else { + Base *base; + + /* object-level */ + *ksc= &ks_contexts[KSC_V3D_OBJECT]; + + /* loop through bases */ + for (base= FIRSTBASE; base; base= base->next) { + if (TESTBASELIB(base)) { + bCommonKeySrc *cks; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + ob= base->object; + cks->id= (ID *)ob; + + /* when ob's keyframes are in an action, default to using 'Object' as achan name */ + if (ob->ipoflag & OB_ACTION_OB) + cks->actname= "Object"; + + /* set ipo-flags */ + // TODO: add checks for lib-linked data + if ((ob->ipo) || (ob->action)) { + if (ob->ipo) { + cks->ipo= ob->ipo; + } + else { + bActionChannel *achan; + + cks->act= ob->action; + achan= get_action_channel(ob->action, cks->actname); + + if (achan && achan->ipo) + cks->ipo= achan->ipo; + } + /* cks->ipo can be NULL while editing */ + if(cks->ipo) { + /* deselect all ipo-curves */ + for (icu= cks->ipo->curve.first; icu; icu= icu->next) { + icu->flag &= ~IPO_SELECT; + } + } + } + } + } + } +} + +/* helper for commonkey_context_get() - get keyingsets for buttons window */ +static void commonkey_context_getsbuts (ListBase *sources, bKeyingContext **ksc) +{ + bCommonKeySrc *cks; + +#if 0 // XXX dunno what's the future of this stuff... + /* check on tab-type */ + switch (G.buts->mainb) { + case CONTEXT_SHADING: /* ------------- Shading buttons ---------------- */ + /* subtabs include "Material", "Texture", "Lamp", "World"*/ + switch (G.buts->tab[CONTEXT_SHADING]) { + case TAB_SHADING_MAT: /* >------------- Material Tab -------------< */ + { + Material *ma= editnode_get_active_material(G.buts->lockpoin); + + if (ma) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)ma; + cks->ipo= ma->ipo; + cks->map= texchannel_to_adrcode(ma->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_MAT]; + return; + } + } + break; + case TAB_SHADING_WORLD: /* >------------- World Tab -------------< */ + { + World *wo= G.buts->lockpoin; + + if (wo) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)wo; + cks->ipo= wo->ipo; + cks->map= texchannel_to_adrcode(wo->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_WO]; + return; + } + } + break; + case TAB_SHADING_LAMP: /* >------------- Lamp Tab -------------< */ + { + Lamp *la= G.buts->lockpoin; + + if (la) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)la; + cks->ipo= la->ipo; + cks->map= texchannel_to_adrcode(la->texact); + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_LA]; + return; + } + } + break; + case TAB_SHADING_TEX: /* >------------- Texture Tab -------------< */ + { + Tex *tex= G.buts->lockpoin; + + if (tex) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set data */ + cks->id= (ID *)tex; + cks->ipo= tex->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_TEX]; + return; + } + } + break; + } + break; + + case CONTEXT_OBJECT: /* ------------- Object buttons ---------------- */ + { + Object *ob= OBACT; + + if (ob) { + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + cks->id= (ID *)ob; + cks->ipo= ob->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_OB]; + return; + } + } + break; + + case CONTEXT_EDITING: /* ------------- Editing buttons ---------------- */ + { + Object *ob= OBACT; + + if ((ob) && (ob->type==OB_CAMERA) && (G.buts->lockpoin)) { /* >---------------- camera buttons ---------------< */ + Camera *ca= G.buts->lockpoin; + + /* add new keyframing destination */ + cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc"); + BLI_addtail(sources, cks); + + /* set id-block to key to */ + cks->id= (ID *)ca; + cks->ipo= ca->ipo; + + /* set keyingsets */ + *ksc= &ks_contexts[KSC_BUTS_CAM]; + return; + } + } + break; + } +#endif // XXX end of buttons stuff to port... + + /* if nothing happened... */ + *ksc= NULL; +} + + +/* get keyingsets for appropriate context */ +static void commonkey_context_get (ScrArea *sa, short mode, ListBase *sources, bKeyingContext **ksc) +{ + /* check view type */ + switch (sa->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + commonkey_context_getv3d(sources, ksc); + } + break; + + /* buttons view */ + case SPACE_BUTS: + { + commonkey_context_getsbuts(sources, ksc); + } + break; + + /* spaces with their own methods */ + case SPACE_IPO: + //if (mode == COMMONKEY_MODE_INSERT) + // insertkey_editipo(); // XXX old calls... + return; + case SPACE_ACTION: + //if (mode == COMMONKEY_MODE_INSERT) + // insertkey_action(); // XXX old calls... + return; + + /* timeline view - keyframe buttons */ + case SPACE_TIME: + { + ScrArea *sab; + int bigarea= 0; + + /* try to find largest 3d-view available + * (mostly of the time, this is what when user will want this, + * as it's a standard feature in all other apps) + */ + //sab= find_biggest_area_of_type(SPACE_VIEW3D); + sab= NULL; // XXX for now... + if (sab) { + commonkey_context_getv3d(sources, ksc); + return; + } + + /* if not found, sab is now NULL, so perform own biggest area test */ + for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { // XXX this has changed! + int area= sa->winx * sa->winy; + + if (sa->spacetype != SPACE_TIME) { + if ( (!sab) || (area > bigarea) ) { + sab= sa; + bigarea= area; + } + } + } + + /* use whichever largest area was found (it shouldn't be a time window) */ + if (sab) + commonkey_context_get(sab, mode, sources, ksc); + } + break; + } +} + +/* flush updates after all operations */ +static void commonkey_context_finish (ListBase *sources) +{ + /* check view type */ + switch (curarea->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + /* either pose or object level */ + if (OBACT && (OBACT->pose)) { + Object *ob= OBACT; + + /* recalculate ipo handles, etc. */ + if (ob->action) + remake_action_ipos(ob->action); + + /* recalculate bone-paths on adding new keyframe? */ + // TODO: currently, there is no setting to turn this on/off globally + if (ob->pose->flag & POSE_RECALCPATHS) + pose_recalculate_paths(ob); + } + else { + bCommonKeySrc *cks; + + /* loop over bases (as seen in sources) */ + for (cks= sources->first; cks; cks= cks->next) { + Object *ob= (Object *)cks->id; + + /* simply set recalc flag */ + ob->recalc |= OB_RECALC_OB; + } + } + } + break; + } +} + +/* flush refreshes after undo */ +static void commonkey_context_refresh (void) +{ + /* check view type */ + switch (curarea->spacetype) { + /* 3d view - first one tested as most often used */ + case SPACE_VIEW3D: + { + /* do refreshes */ + DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0); + + //allspace(REMAKEIPO, 0); + //allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWMARKER, 0); + } + break; + + /* buttons window */ + case SPACE_BUTS: + { + //allspace(REMAKEIPO, 0); + //allqueue(REDRAWVIEW3D, 0); + //allqueue(REDRAWMARKER, 0); + } + break; + } +} + +/* --- */ + +/* Build menu-string of available keying-sets (allocates memory for string) + * NOTE: mode must not be longer than 64 chars + */ +static char *build_keyingsets_menu (bKeyingContext *ksc, const char mode[48]) +{ + DynStr *pupds= BLI_dynstr_new(); + bKeyingSet *ks; + char buf[64]; + char *str; + int i, n; + + /* add title first */ + BLI_snprintf(buf, 64, "%s Key %%t|", mode); + BLI_dynstr_append(pupds, buf); + + /* loop through keyingsets, adding them */ + for (ks=ksc->keyingsets, i=0, n=1; i < ksc->tot; ks++, i++, n++) { + /* check if keyingset can be used */ + if (ks->flag == -1) { + /* optional separator? */ + if (ks->include_cb) { + if (ks->include_cb(ks, mode)) { + BLI_snprintf( buf, 64, "%s%s", ks->name, ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + else { + BLI_snprintf( buf, 64, "%%l%s", ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + else if ( (ks->include_cb==NULL) || (ks->include_cb(ks, mode)) ) { + /* entry can be included */ + BLI_dynstr_append(pupds, ks->name); + + /* check if special "shapekey" entry */ + if (ks->flag == -3) + BLI_snprintf( buf, 64, "%%x0%s", ((n < ksc->tot)?"|":"") ); + else + BLI_snprintf( buf, 64, "%%x%d%s", n, ((n < ksc->tot)?"|":"") ); + BLI_dynstr_append(pupds, buf); + } + } + + /* convert to normal MEM_malloc'd string */ + str= BLI_dynstr_get_cstring(pupds); + BLI_dynstr_free(pupds); + + return str; +} + +/* Get the keying set that was chosen by the user from the menu */ +static bKeyingSet *get_keyingset_fromcontext (bKeyingContext *ksc, short index) +{ + /* check if index is valid */ + if (ELEM(NULL, ksc, ksc->keyingsets)) + return NULL; + if ((index < 1) || (index > ksc->tot)) + return NULL; + + /* index starts from 1, and should directly correspond to keyingset in array */ + return (bKeyingSet *)(ksc->keyingsets + (index - 1)); +} + +/* ---------------- Keyframe Management API -------------------- */ + +/* Display a menu for handling the insertion of keyframes based on the active view */ +// TODO: add back an option for repeating last keytype +void common_modifykey (short mode) +{ + ListBase dsources = {NULL, NULL}; + bKeyingContext *ksc= NULL; + bCommonKeySrc *cks; + bKeyingSet *ks = NULL; + char *menustr, buf[64]; + short menu_nr; + + /* check if mode is valid */ + if (ELEM(mode, COMMONKEY_MODE_INSERT, COMMONKEY_MODE_DELETE)==0) + return; + + /* delegate to other functions or get keyingsets to use + * - if the current area doesn't have its own handling, there will be data returned... + */ + commonkey_context_get(curarea, mode, &dsources, &ksc); + + /* check that there is data to operate on */ + if (ELEM(NULL, dsources.first, ksc)) { + BLI_freelistN(&dsources); + return; + } + + /* get menu and process it */ + if (mode == COMMONKEY_MODE_DELETE) + menustr= build_keyingsets_menu(ksc, "Delete"); + else + menustr= build_keyingsets_menu(ksc, "Insert"); + menu_nr= pupmenu(menustr); + if (menustr) MEM_freeN(menustr); + + /* no item selected or shapekey entry? */ + if (menu_nr < 1) { + /* free temp sources */ + BLI_freelistN(&dsources); + + /* check if insert new shapekey */ + if ((menu_nr == 0) && (mode == COMMONKEY_MODE_INSERT)) + insert_shapekey(OBACT); + else + ksc->lastused= NULL; + + return; + } + else { + /* try to get keyingset */ + ks= get_keyingset_fromcontext(ksc, menu_nr); + + if (ks == NULL) { + BLI_freelistN(&dsources); + return; + } + } + + /* loop over each destination, applying the keying set */ + for (cks= dsources.first; cks; cks= cks->next) { + short success= 0; + + /* special hacks for 'available' option */ + if (ks->flag == -2) { + IpoCurve *icu= NULL, *icn= NULL; + + /* get first IPO-curve */ + if (cks->act && cks->actname) { + bActionChannel *achan= get_action_channel(cks->act, cks->actname); + + // FIXME: what about constraint channels? + if (achan && achan->ipo) + icu= achan->ipo->curve.first; + } + else if(cks->ipo) + icu= cks->ipo->curve.first; + + /* we get adrcodes directly from IPO curves (see method below...) */ + for (; icu; icu= icn) { + short flag; + + /* get next ipo-curve in case current is deleted */ + icn= icu->next; + + /* insert mode or delete mode */ + if (mode == COMMONKEY_MODE_DELETE) { + /* local flags only add on to global flags */ + flag = 0; + + /* delete keyframe */ + success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); + } + else { + /* local flags only add on to global flags */ + flag = ks->flag; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; + // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; + + /* insert keyframe */ + success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag); + } + } + } + else { + bKS_AdrcodeGetter kag; + short (*get_next_adrcode)(bKS_AdrcodeGetter *); + int adrcode; + + /* initialise keyingset channel iterator */ + ks_adrcodegetter_init(&kag, ks, cks); + + /* get iterator - only one can be in use at a time... the flags should be mutually exclusive in this regard */ + if (ks->flag & COMMONKEY_PCHANROT) + get_next_adrcode= ks_getnextadrcode_pchanrot; + else if (ks->flag & COMMONKEY_ADDMAP) + get_next_adrcode= ks_getnextadrcode_addmap; + else + get_next_adrcode= ks_getnextadrcode_default; + + /* loop over channels available in keyingset */ + for (adrcode= get_next_adrcode(&kag); adrcode > 0; adrcode= get_next_adrcode(&kag)) { + short flag; + + /* insert mode or delete mode */ + if (mode == COMMONKEY_MODE_DELETE) { + /* local flags only add on to global flags */ + flag = 0; + //flag &= ~COMMONKEY_MODES; + + /* delete keyframe */ + success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); + } + else { + /* local flags only add on to global flags */ + flag = ks->flag; + if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX; + if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED; + // if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE; + flag &= ~COMMONKEY_MODES; + + /* insert keyframe */ + success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag); + } + } + } + + /* special handling for some key-sources */ + if (success) { + /* set pose recalc-paths flag */ + if (cks->pchan) { + Object *ob= (Object *)cks->id; + bPoseChannel *pchan= cks->pchan; + + /* set flag to trigger path recalc */ + if (pchan->path) + ob->pose->flag |= POSE_RECALCPATHS; + + /* clear unkeyed flag (it doesn't matter if it's set or not) */ + if (pchan->bone) + pchan->bone->flag &= ~BONE_UNKEYED; + } + } + } + + /* apply post-keying flushes for this data sources */ + commonkey_context_finish(&dsources); + ksc->lastused= ks; + + /* free temp data */ + BLI_freelistN(&dsources); + + /* undo pushes */ + if (mode == COMMONKEY_MODE_DELETE) + BLI_snprintf(buf, 64, "Delete %s Key", ks->name); + else + BLI_snprintf(buf, 64, "Insert %s Key", ks->name); + BIF_undo_push(buf); + + /* queue updates for contexts */ + commonkey_context_refresh(); +} + +/* ---- */ + +/* used to insert keyframes from any view */ +void common_insertkey (void) +{ + common_modifykey(COMMONKEY_MODE_INSERT); +} + +/* used to insert keyframes from any view */ +void common_deletekey (void) +{ + common_modifykey(COMMONKEY_MODE_DELETE); +} + +/* ************************************************** */ +/* KEYFRAME DETECTION */ + +/* --------------- API/Per-Datablock Handling ------------------- */ + +/* Checks whether an IPO-block has a keyframe for a given frame + * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... + */ +short ipo_frame_has_keyframe (Ipo *ipo, float frame, short filter) +{ + IpoCurve *icu; + + /* can only find if there is data */ + if (ipo == NULL) + return 0; + + /* if only check non-muted, check if muted */ + if ((filter & ANIMFILTER_MUTED) || (ipo->muteipo)) + return 0; + + /* loop over IPO-curves, using binary-search to try to find matches + * - this assumes that keyframes are only beztriples + */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + /* only check if there are keyframes (currently only of type BezTriple) */ + if (icu->bezt) { + /* we either include all regardless of muting, or only non-muted */ + if ((filter & ANIMFILTER_MUTED) || (icu->flag & IPO_MUTE)==0) { + short replace = -1; + int i = binarysearch_bezt_index(icu->bezt, frame, icu->totvert, &replace); + + /* binarysearch_bezt_index will set replace to be 0 or 1 + * - obviously, 1 represents a match + */ + if (replace) { + /* sanity check: 'i' may in rare cases exceed arraylen */ + if ((i >= 0) && (i < icu->totvert)) + return 1; + } + } + } + } + + /* nothing found */ + return 0; +} + +/* Checks whether an action-block has a keyframe for a given frame + * Since we're only concerned whether a keyframe exists, we can simply loop until a match is found... + */ +short action_frame_has_keyframe (bAction *act, float frame, short filter) +{ + bActionChannel *achan; + + /* error checking */ + if (act == NULL) + return 0; + + /* check thorugh action-channels for match */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* we either include all regardless of muting, or only non-muted + * - here we include 'hidden' channels in the muted definition + */ + if ((filter & ANIMFILTER_MUTED) || (achan->flag & ACHAN_HIDDEN)==0) { + if (ipo_frame_has_keyframe(achan->ipo, frame, filter)) + return 1; + } + } + + /* nothing found */ + return 0; +} + +/* Checks whether an Object has a keyframe for a given frame */ +short object_frame_has_keyframe (Object *ob, float frame, short filter) +{ + /* error checking */ + if (ob == NULL) + return 0; + + /* check for an action - actions take priority over normal IPO's */ + if (ob->action) { + float aframe; + + /* apply nla-action scaling if needed */ + if ((ob->nlaflag & OB_NLA_OVERRIDE) && (ob->nlastrips.first)) + aframe= get_action_frame(ob, frame); + else + aframe= frame; + + /* priority check here goes to pose-channel checks (for armatures) */ + if ((ob->pose) && (ob->flag & OB_POSEMODE)) { + /* only relevant check here is to only show active... */ + if (filter & ANIMFILTER_ACTIVE) { + bPoseChannel *pchan= get_active_posechannel(ob); + bActionChannel *achan= (pchan) ? get_action_channel(ob->action, pchan->name) : NULL; + + /* since we're only interested in whether the selected one has any keyframes... */ + return (achan && ipo_frame_has_keyframe(achan->ipo, aframe, filter)); + } + } + + /* for everything else, just use the standard test (only return if success) */ + if (action_frame_has_keyframe(ob->action, aframe, filter)) + return 1; + } + else if (ob->ipo) { + /* only return if success */ + if (ipo_frame_has_keyframe(ob->ipo, frame, filter)) + return 1; + } + + /* try shapekey keyframes (if available, and allowed by filter) */ + if ( !(filter & ANIMFILTER_LOCAL) && !(filter & ANIMFILTER_NOSKEY) ) { + Key *key= ob_get_key(ob); + + /* shapekeys can have keyframes ('Relative Shape Keys') + * or depend on time (old 'Absolute Shape Keys') + */ + + /* 1. test for relative (with keyframes) */ + if (id_frame_has_keyframe((ID *)key, frame, filter)) + return 1; + + /* 2. test for time */ + // TODO... yet to be implemented (this feature may evolve before then anyway) + } + + /* try materials */ + if ( !(filter & ANIMFILTER_LOCAL) && !(filter & ANIMFILTER_NOMAT) ) { + /* if only active, then we can skip a lot of looping */ + if (filter & ANIMFILTER_ACTIVE) { + Material *ma= give_current_material(ob, (ob->actcol + 1)); + + /* we only retrieve the active material... */ + if (id_frame_has_keyframe((ID *)ma, frame, filter)) + return 1; + } + else { + int a; + + /* loop over materials */ + for (a=0; a<ob->totcol; a++) { + Material *ma= give_current_material(ob, a+1); + + if (id_frame_has_keyframe((ID *)ma, frame, filter)) + return 1; + } + } + } + + /* nothing found */ + return 0; +} + +/* --------------- API ------------------- */ + +/* Checks whether a keyframe exists for the given ID-block one the given frame */ +short id_frame_has_keyframe (ID *id, float frame, short filter) +{ + /* error checking */ + if (id == NULL) + return 0; + + /* check for a valid id-type */ + switch (GS(id->name)) { + /* animation data-types */ + case ID_IP: /* ipo */ + return ipo_frame_has_keyframe((Ipo *)id, frame, filter); + case ID_AC: /* action */ + return action_frame_has_keyframe((bAction *)id, frame, filter); + + case ID_OB: /* object */ + return object_frame_has_keyframe((Object *)id, frame, filter); + + case ID_MA: /* material */ + { + Material *ma= (Material *)id; + + /* currently, material's only have an ipo-block */ + return ipo_frame_has_keyframe(ma->ipo, frame, filter); + } + break; + + case ID_KE: /* shapekey */ + { + Key *key= (Key *)id; + + /* currently, shapekey's only have an ipo-block */ + return ipo_frame_has_keyframe(key->ipo, frame, filter); + } + break; + } + + /* no keyframe found */ + return 0; +} + +/* ************************************************** */ + +#endif // XXX reenable this file again later... diff --git a/source/blender/editors/util/ed_markers.c b/source/blender/editors/animation/anim_markers.c index 6c27219c9c7..a428bc7559a 100644 --- a/source/blender/editors/util/ed_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -105,7 +105,7 @@ static void draw_marker(View2D *v2d, TimeMarker *marker, int cfra, int flag) glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - /* verticle line */ + /* vertical line */ if (flag & DRAW_MARKERS_LINES) { setlinestyle(3); if(marker->flag & SELECT) diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h new file mode 100644 index 00000000000..70f9011417a --- /dev/null +++ b/source/blender/editors/include/ED_anim_api.h @@ -0,0 +1,172 @@ +/** + * $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) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_ANIM_API_H +#define ED_ANIM_API_H + +struct ID; +struct ListBase; +struct bContext; +struct View2D; +struct bActionGroup; + +/* ************************************************ */ +/* ANIMATION CHANNEL FILTERING */ + +/* --------------- Data Types -------------------- */ + +/* This struct defines a structure used for quick and uniform access for + * channels of animation data + */ +typedef struct bAnimListElem { + struct bAnimListElem *next, *prev; + + void *data; /* source data this elem represents */ + int type; /* one of the ANIMTYPE_* values */ + int flag; /* copy of elem's flags for quick access */ + int index; /* copy of adrcode where applicable */ + + void *key_data; /* motion data - ipo or ipo-curve */ + short datatype; /* type of motion data to expect */ + + struct ID *id; /* ID block (ID_SC, ID_SCE, or ID_OB) that owns the channel */ + struct bActionGroup *grp; /* action group that owns the channel (only for Action/Dopesheet) */ + + void *owner; /* will either be an action channel or fake ipo-channel (for keys) */ + short ownertype; /* type of owner */ +} bAnimListElem; + + +/* Some types for easier type-testing */ +typedef enum eAnim_ChannelType { + ANIMTYPE_NONE= 0, + ANIMTYPE_SPECIALDATA, + + ANIMTYPE_OBJECT, + ANIMTYPE_GROUP, + + ANIMTYPE_FILLIPO, + ANIMTYPE_FILLCON, + + ANIMTYPE_FILLACTD, + ANIMTYPE_FILLIPOD, + ANIMTYPE_FILLCOND, + ANIMTYPE_FILLMATD, + + ANIMTYPE_DSMAT, + ANIMTYPE_DSLAM, + ANIMTYPE_DSCAM, + ANIMTYPE_DSCUR, + ANIMTYPE_DSSKEY, + + ANIMTYPE_ACHAN, + ANIMTYPE_CONCHAN, + ANIMTYPE_CONCHAN2, + ANIMTYPE_ICU, + ANIMTYPE_IPO, + + ANIMTYPE_SHAPEKEY, + ANIMTYPE_GPDATABLOCK, + ANIMTYPE_GPLAYER, +} eAnim_ChannelType; + +/* types of keyframe data in bAnimListElem */ +typedef enum eAnim_KeyType { + ALE_NONE = 0, /* no keyframe data */ + ALE_IPO, /* IPO block */ + ALE_ICU, /* IPO-Curve block */ + ALE_GPFRAME, /* Grease Pencil Frames */ + + // XXX the following are for summaries... should these be kept? + ALE_OB, /* Object summary */ + ALE_ACT, /* Action summary */ + ALE_GROUP, /* Action Group summary */ +} eAnim_KeyType; + +/* Main Data container types */ +typedef enum eAnimCont_Types { + ANIMCONT_NONE = 0, /* invalid or no data */ + ANIMCONT_ACTION, /* action (bAction) */ + ANIMCONT_SHAPEKEY, /* shapekey (Key) */ + ANIMCONT_GPENCIL, /* grease pencil (screen) */ + ANIMCONT_DOPESHEET, /* dopesheet (bDopesheet) */ +} eAnimCont_Types; + +/* filtering flags - under what circumstances should a channel be added */ +typedef enum eAnimFilter_Flags { + ALEFILTER_VISIBLE = (1<<0), /* should channels be visible */ + ALEFILTER_SEL = (1<<1), /* should channels be selected */ + ALEFILTER_FOREDIT = (1<<2), /* does editable status matter */ + ALEFILTER_CHANNELS = (1<<3), /* do we only care that it is a channel */ + ALEFILTER_IPOKEYS = (1<<4), /* only channels referencing ipo's */ + ALEFILTER_ONLYICU = (1<<5), /* only reference ipo-curves */ + ALEFILTER_FORDRAWING = (1<<6), /* make list for interface drawing */ + ALEFILTER_ACTGROUPED = (1<<7), /* belongs to the active actiongroup */ +} eAnimFilter_Flags; + + +/* ---------------- API -------------------- */ + +/* Obtain list of filtered Animation channels to operate on */ +void animdata_filter(struct ListBase *act_data, int filter_mode, void *data, short datatype); + +/* Obtain current anim-data context from Blender Context info */ +void *animdata_get_context(const struct bContext *C, short *datatype); + +/* ************************************************ */ +/* DRAWING API */ +// XXX should this get its own header file? + +/* ---------- Current Frame Drawing ---------------- */ + +/* flags for Current Frame Drawing */ +enum { + /* plain time indicator with no special indicators */ + DRAWCFRA_PLAIN = 0, + /* draw box indicating current frame number */ + DRAWCFRA_SHOW_NUMBOX = (1<<0), + /* time indication in seconds or frames */ + DRAWCFRA_UNIT_SECONDS = (1<<1), + /* show time-offset line */ + DRAWCFRA_SHOW_TIMEOFS = (1<<2), +} eAnimEditDraw_CurrentFrame; + +/* main call to draw current-frame indicator in an Animation Editor */ +void ANIM_draw_cfra(const bContext *C, struct View2D *v2d, short flag); + +/* ------------- Preview Range Drawing -------------- */ + +// XXX should preview range get its own file? + +/* main call to draw preview range curtains */ +void ANIM_draw_previewrange(const bContext *C, struct View2D *v2d); + +/* ************************************************* */ + +#endif /* ED_ANIM_API_H */ + diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h new file mode 100644 index 00000000000..29d19cd18e3 --- /dev/null +++ b/source/blender/editors/include/ED_keyframing.h @@ -0,0 +1,134 @@ +/** + * $Id: BIF_keyframing.h 17216 2008-10-29 11:20:02Z aligorith $ + * + * ***** 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) 2008, Blender Foundation + * This is a new part of Blender (with some old code) + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BIF_KEYFRAMING_H +#define BIF_KEYFRAMING_H + +struct ListBase; +struct ID; + +struct IpoCurve; +struct BezTriple; + +/* ************ Keyframing Management **************** */ + +/* Lesser Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it already + * exists, and there is a beztriple that can be directly copied into the array. + */ +int insert_bezt_icu(struct IpoCurve *icu, struct BezTriple *bezt); + +/* Main Keyframing API call: + * Use this when validation of necessary animation data isn't necessary as it + * already exists. It will insert a keyframe using the current value being keyframed. + */ +void insert_vert_icu(struct IpoCurve *icu, float x, float y, short flag); + + +/* flags for use by keyframe creation/deletion calls */ +enum { + /* used by isnertkey() and insert_vert_icu() */ + INSERTKEY_NEEDED = (1<<0), /* only insert keyframes where they're needed */ + INSERTKEY_MATRIX = (1<<1), /* insert 'visual' keyframes where possible/needed */ + INSERTKEY_FAST = (1<<2), /* don't recalculate handles,etc. after adding key */ + INSERTKEY_FASTR = (1<<3), /* don't realloc mem (or increase count, as array has already been set out) */ + INSERTKEY_REPLACE = (1<<4), /* only replace an existing keyframe (this overrides INSERTKEY_NEEDED) */ + + /* used by common_*key() functions - Note: these are generally mutually exclusive (only one will work at a time) */ + COMMONKEY_ADDMAP = (1<<10), /* common key: add texture-slot offset bitflag to adrcode before use */ + COMMONKEY_PCHANROT = (1<<11), /* common key: extend channel list using relevant pchan-rotations */ + /* all possible items for common_*key() functions */ + COMMONKEY_MODES = (COMMONKEY_ADDMAP|COMMONKEY_PCHANROT) +} eInsertKeyFlags; + +/* -------- */ + +/* Main Keyframing API calls: + * Use this to create any necessary animation data, and then insert a keyframe + * using the current value being keyframed, in the relevant place. Returns success. + */ + // TODO: adapt this for new data-api -> this blocktype, etc. stuff is evil! +short insertkey(struct ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag); + +/* Main Keyframing API call: + * Use this to delete keyframe on current frame for relevant channel. Will perform checks just in case. + */ +short deletekey(struct ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag); + + +/* Main Keyframe Management calls: + * These handle keyframes management from various spaces. They will handle the menus + * required for each space. + */ +void common_insertkey(void); +void common_deletekey(void); + +/* ************ Auto-Keyframing ********************** */ +/* Notes: + * - All the defines for this (User-Pref settings and Per-Scene settings) + * are defined in DNA_userdef_types.h + * - Scene settings take presidence over those for userprefs, with old files + * inheriting userpref settings for the scene settings + * - "On/Off + Mode" are stored per Scene, but "settings" are currently stored + * as userprefs + */ + +/* Auto-Keying macros for use by various tools */ + /* check if auto-keyframing is enabled (per scene takes presidence) */ +#define IS_AUTOKEY_ON ((scene) ? (scene->autokey_mode & AUTOKEY_ON) : (U.autokey_mode & AUTOKEY_ON)) + /* check the mode for auto-keyframing (per scene takes presidence) */ +#define IS_AUTOKEY_MODE(mode) ((scene) ? (scene->autokey_mode == AUTOKEY_MODE_##mode) : (U.autokey_mode == AUTOKEY_MODE_##mode)) + /* check if a flag is set for auto-keyframing (as userprefs only!) */ +#define IS_AUTOKEY_FLAG(flag) (U.autokey_flag & AUTOKEY_FLAG_##flag) + +/* ************ Keyframe Checking ******************** */ + +/* Main Keyframe Checking API call: + * Checks whether a keyframe exists for the given ID-block one the given frame. + * - It is recommended to call this method over the other keyframe-checkers directly, + * in case some detail of the implementation changes... + * - frame: the value of this is quite often result of frame_to_float(CFRA) + */ +short id_frame_has_keyframe(struct ID *id, float frame, short filter); + +/* filter flags for id_cfra_has_keyframe + * + * WARNING: do not alter order of these, as also stored in files + * (for v3d->keyflags) + */ +enum { + /* general */ + ANIMFILTER_LOCAL = (1<<0), /* only include locally available anim data */ + ANIMFILTER_MUTED = (1<<1), /* include muted elements */ + ANIMFILTER_ACTIVE = (1<<2), /* only include active-subelements */ + + /* object specific */ + ANIMFILTER_NOMAT = (1<<9), /* don't include material keyframes */ + ANIMFILTER_NOSKEY = (1<<10), /* don't include shape keys (for geometry) */ +} eAnimFilterFlags; + +#endif /* BIF_KEYFRAMING_H */ diff --git a/source/blender/editors/space_action/SConscript b/source/blender/editors/space_action/SConscript index f1152e95d0c..3d22e8ed5ab 100644 --- a/source/blender/editors/space_action/SConscript +++ b/source/blender/editors/space_action/SConscript @@ -6,4 +6,4 @@ sources = env.Glob('*.c') incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' -env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), [], libtype=['core','intern'], priority=[35, 40] ) +env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), [], libtype=['core','intern'], priority=[33, 37] ) diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 0ebcd88ffb2..12666bbd41b 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -57,6 +57,7 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "ED_anim_api.h" #include "ED_markers.h" #include "action_intern.h" // own include @@ -167,7 +168,7 @@ static void action_main_area_draw(const bContext *C, ARegion *ar) View2DGrid *grid; View2DScrollers *scrollers; float col[3]; - int unit; + short unit=0, flag=0; /* clear and setup matrix */ UI_GetThemeColor3fv(TH_BACK, col); @@ -184,6 +185,13 @@ static void action_main_area_draw(const bContext *C, ARegion *ar) /* data? */ + /* current frame */ + if (saction->flag & SACTION_DRAWTIME) flag |= DRAWCFRA_UNIT_SECONDS; + if ((saction->flag & SACTION_NODRAWCFRANUM)==0) flag |= DRAWCFRA_SHOW_NUMBOX; + ANIM_draw_cfra(C, v2d, flag); + + /* preview range */ + ANIM_draw_previewrange(C, v2d); /* reset view matrix */ UI_view2d_view_restore(C); @@ -298,7 +306,7 @@ void ED_spacetype_action(void) art->init= action_main_area_init; art->draw= action_main_area_draw; art->listener= action_main_area_listener; - art->keymapflag= ED_KEYMAP_VIEW2D; + art->keymapflag= ED_KEYMAP_VIEW2D|ED_KEYMAP_MARKERS; BLI_addhead(&st->regiontypes, art); diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index e89a64faf94..5cadd521b66 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -85,7 +85,8 @@ static void time_draw_cfra_time(const bContext *C, SpaceTime *stime, ARegion *ar static void time_draw_sfra_efra(const bContext *C, SpaceTime *stime, ARegion *ar) { - View2D *v2d= UI_view2d_fromcontext(C); + View2D *v2d= UI_view2d_fromcontext(C); + //Scene *scene= CTX_data_scene(C); /* draw darkened area outside of active timeline * frame range used is preview range or scene range */ diff --git a/source/blender/editors/space_time/time_ops.c b/source/blender/editors/space_time/time_ops.c index 30b0e808e05..e8704457c00 100644 --- a/source/blender/editors/space_time/time_ops.c +++ b/source/blender/editors/space_time/time_ops.c @@ -181,16 +181,18 @@ void ED_TIME_OT_change_frame(wmOperatorType *ot) static int toggle_time_exec(bContext *C, wmOperator *op) { - SpaceTime *stime; + SpaceTime *stime= (SpaceTime *)CTX_wm_space_data(C); + ScrArea *curarea= CTX_wm_area(C); - if (ELEM(NULL, CTX_wm_area(C), CTX_wm_space_data(C))) + if (ELEM(NULL, curarea, stime)) return OPERATOR_CANCELLED; /* simply toggle draw frames flag for now */ - // XXX in past, this displayed menu to choose... (for later!) - stime= (SpaceTime*)CTX_wm_space_data(C); + // in past, this asked user to choose in a menu beforehand, but that is clumsy stime->flag ^= TIME_DRAWFRAMES; + ED_area_tag_redraw(curarea); + return OPERATOR_FINISHED; } |