diff options
Diffstat (limited to 'source/blender/editors/animation/anim_keyframes_draw.c')
-rw-r--r-- | source/blender/editors/animation/anim_keyframes_draw.c | 701 |
1 files changed, 701 insertions, 0 deletions
diff --git a/source/blender/editors/animation/anim_keyframes_draw.c b/source/blender/editors/animation/anim_keyframes_draw.c new file mode 100644 index 00000000000..2f8d1be784e --- /dev/null +++ b/source/blender/editors/animation/anim_keyframes_draw.c @@ -0,0 +1,701 @@ +/** + * $Id: drawaction.c 17746 2008-12-08 11:19:44Z 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) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/* System includes ----------------------------------------------------- */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <float.h> + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +/* Types --------------------------------------------------------------- */ + +#include "DNA_listBase.h" +#include "DNA_action_types.h" +#include "DNA_armature_types.h" +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_ipo_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" +#include "DNA_constraint_types.h" +#include "DNA_key_types.h" +#include "DNA_lamp_types.h" +#include "DNA_material_types.h" +#include "DNA_userdef_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_action.h" +#include "BKE_depsgraph.h" +#include "BKE_ipo.h" +#include "BKE_key.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_global.h" // XXX remove me! +#include "BKE_context.h" +#include "BKE_utildefines.h" + +/* Everything from source (BIF, BDR, BSE) ------------------------------ */ + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "UI_interface.h" +#include "UI_interface_icons.h" +#include "UI_resources.h" +#include "UI_text.h" +#include "UI_view2d.h" + +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_keyframes_draw.h" +#include "ED_screen.h" +#include "ED_space_api.h" + + +#if 0 // XXX old includes for reference only + #include "BIF_editaction.h" + #include "BIF_editkey.h" + #include "BIF_editnla.h" + #include "BIF_drawgpencil.h" + #include "BIF_keyframing.h" + #include "BIF_language.h" + #include "BIF_space.h" + + #include "BDR_editcurve.h" + #include "BDR_gpencil.h" + + #include "BSE_drawnla.h" + #include "BSE_drawipo.h" + #include "BSE_drawview.h" + #include "BSE_editaction_types.h" + #include "BSE_editipo.h" + #include "BSE_headerbuttons.h" + #include "BSE_time.h" + #include "BSE_view.h" +#endif // XXX old defines for reference only + +/* *************************** Keyframe Drawing *************************** */ + +static void add_bezt_to_keycolumnslist(ListBase *keys, BezTriple *bezt) +{ + /* The equivilant of add_to_cfra_elem except this version + * makes ActKeyColumns - one of the two datatypes required + * for action editor drawing. + */ + ActKeyColumn *ak, *akn; + + if (ELEM(NULL, keys, bezt)) return; + + /* try to any existing key to replace, or where to insert after */ + for (ak= keys->last; ak; ak= ak->prev) { + /* do because of double keys */ + if (ak->cfra == bezt->vec[1][0]) { + /* set selection status and 'touched' status */ + if (BEZSELECTED(bezt)) ak->sel = SELECT; + ak->modified += 1; + + return; + } + else if (ak->cfra < bezt->vec[1][0]) break; + } + + /* add new block */ + akn= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); + if (ak) BLI_insertlinkafter(keys, ak, akn); + else BLI_addtail(keys, akn); + + akn->cfra= bezt->vec[1][0]; + akn->modified += 1; + + // TODO: handle type = bezt->h1 or bezt->h2 + akn->handle_type= 0; + + if (BEZSELECTED(bezt)) + akn->sel = SELECT; + else + akn->sel = 0; +} + +static void add_bezt_to_keyblockslist(ListBase *blocks, IpoCurve *icu, int index) +{ + /* The equivilant of add_to_cfra_elem except this version + * makes ActKeyBlocks - one of the two datatypes required + * for action editor drawing. + */ + ActKeyBlock *ab, *abn; + BezTriple *beztn=NULL, *prev=NULL; + BezTriple *bezt; + int v; + + /* get beztriples */ + beztn= (icu->bezt + index); + + /* we need to go through all beztriples, as they may not be in order (i.e. during transform) */ + for (v=0, bezt=icu->bezt; v<icu->totvert; v++, bezt++) { + /* skip if beztriple is current */ + if (v != index) { + /* check if beztriple is immediately before */ + if (beztn->vec[1][0] > bezt->vec[1][0]) { + /* check if closer than previous was */ + if (prev) { + if (prev->vec[1][0] < bezt->vec[1][0]) + prev= bezt; + } + else { + prev= bezt; + } + } + } + } + + /* check if block needed - same value(s)? + * -> firstly, handles must have same central value as each other + * -> secondly, handles which control that section of the curve must be constant + */ + if ((!prev) || (!beztn)) return; + if (IS_EQ(beztn->vec[1][1], prev->vec[1][1])==0) return; + if (IS_EQ(beztn->vec[1][1], beztn->vec[0][1])==0) return; + if (IS_EQ(prev->vec[1][1], prev->vec[2][1])==0) return; + + /* try to find a keyblock that starts on the previous beztriple + * Note: we can't search from end to try to optimise this as it causes errors there's + * an A ___ B |---| B situation + */ + // FIXME: here there is a bug where we are trying to get the summary for the following channels + // A|--------------|A ______________ B|--------------|B + // A|------------------------------------------------|A + // A|----|A|---|A|-----------------------------------|A + for (ab= blocks->first; ab; ab= ab->next) { + /* check if alter existing block or add new block */ + if (ab->start == prev->vec[1][0]) { + /* set selection status and 'touched' status */ + if (BEZSELECTED(beztn)) ab->sel = SELECT; + ab->modified += 1; + + return; + } + else if (ab->start < prev->vec[1][0]) break; + } + + /* add new block */ + abn= MEM_callocN(sizeof(ActKeyBlock), "ActKeyBlock"); + if (ab) BLI_insertlinkbefore(blocks, ab, abn); + else BLI_addtail(blocks, abn); + + abn->start= prev->vec[1][0]; + abn->end= beztn->vec[1][0]; + abn->val= beztn->vec[1][1]; + + if (BEZSELECTED(prev) || BEZSELECTED(beztn)) + abn->sel = SELECT; + else + abn->sel = 0; + abn->modified = 1; +} + +/* helper function - find actkeycolumn that occurs on cframe */ +static ActKeyColumn *cfra_find_actkeycolumn (ListBase *keys, float cframe) +{ + ActKeyColumn *ak, *ak2; + + if (keys==NULL) + return NULL; + + /* search from both ends at the same time, and stop if we find match or if both ends meet */ + for (ak=keys->first, ak2=keys->last; ak && ak2; ak=ak->next, ak2=ak2->prev) { + /* return whichever end encounters the frame */ + if (ak->cfra == cframe) + return ak; + if (ak2->cfra == cframe) + return ak2; + + /* no matches on either end, so return NULL */ + if (ak == ak2) + return NULL; + } + + return NULL; +} + +#if 0 // disabled, as some intel cards have problems with this +/* Draw a simple diamond shape with a filled in center (in screen space) */ +static void draw_key_but(int x, int y, short w, short h, int sel) +{ + int xmin= x, ymin= y; + int xmax= x+w-1, ymax= y+h-1; + int xc= (xmin+xmax)/2, yc= (ymin+ymax)/2; + + /* interior - hardcoded colors (for selected and unselected only) */ + if (sel) glColor3ub(0xF1, 0xCA, 0x13); + else glColor3ub(0xE9, 0xE9, 0xE9); + + glBegin(GL_QUADS); + glVertex2i(xc, ymin); + glVertex2i(xmax, yc); + glVertex2i(xc, ymax); + glVertex2i(xmin, yc); + glEnd(); + + + /* outline */ + glColor3ub(0, 0, 0); + + glBegin(GL_LINE_LOOP); + glVertex2i(xc, ymin); + glVertex2i(xmax, yc); + glVertex2i(xc, ymax); + glVertex2i(xmin, yc); + glEnd(); +} +#endif + +static void draw_keylist(gla2DDrawInfo *di, ListBase *keys, ListBase *blocks, float ypos) +{ + ActKeyColumn *ak; + ActKeyBlock *ab; + + glEnable(GL_BLEND); + + /* draw keyblocks */ + if (blocks) { + for (ab= blocks->first; ab; ab= ab->next) { + short startCurves, endCurves, totCurves; + + /* find out how many curves occur at each keyframe */ + ak= cfra_find_actkeycolumn(keys, ab->start); + startCurves = (ak)? ak->totcurve: 0; + + ak= cfra_find_actkeycolumn(keys, ab->end); + endCurves = (ak)? ak->totcurve: 0; + + /* only draw keyblock if it appears in at all of the keyframes at lowest end */ + if (!startCurves && !endCurves) + continue; + else + totCurves = (startCurves>endCurves)? endCurves: startCurves; + + if (ab->totcurve >= totCurves) { + int sc_xa, sc_xb, sc_y; + + /* get co-ordinates of block */ + // XXX only use x-coordinates... y are dummy ones! + gla2DDrawTranslatePt(di, ab->start, ypos, &sc_xa, &sc_y); + gla2DDrawTranslatePt(di, ab->end, ypos, &sc_xb, &sc_y); + + /* draw block */ + if (ab->sel) + UI_ThemeColor4(TH_STRIP_SELECT); + else + UI_ThemeColor4(TH_STRIP); + glRectf((float)sc_xa, (float)ypos-3, (float)sc_xb, (float)ypos+5); + } + } + } + + /* draw keys */ + if (keys) { + for (ak= keys->first; ak; ak= ak->next) { + int sc_x, sc_y; + + /* get co-ordinate to draw at */ + // XXX only use x-coordinates... y are dummy ones! + gla2DDrawTranslatePt(di, ak->cfra, ypos, &sc_x, &sc_y); + + /* draw using icons - old way which is slower but more proven */ + if (ak->sel & SELECT)UI_icon_draw_aspect((float)sc_x-7, (float)ypos-6, ICON_SPACE2, 1.0f); + else UI_icon_draw_aspect((float)sc_x-7, (float)ypos-6, ICON_SPACE3, 1.0f); + + /* draw using OpenGL - slightly uglier but faster */ + // NOTE: disabled for now, as some intel cards seem to have problems with this + //draw_key_but(sc_x-5, sc_y-4, 11, 11, (ak->sel & SELECT)); + } + } + + glDisable(GL_BLEND); +} + +void draw_object_channel(gla2DDrawInfo *di, ActKeysInc *aki, Object *ob, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + ob_to_keylist(ob, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_ipo_channel(gla2DDrawInfo *di, ActKeysInc *aki, Ipo *ipo, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + ipo_to_keylist(ipo, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_icu_channel(gla2DDrawInfo *di, ActKeysInc *aki, IpoCurve *icu, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + icu_to_keylist(icu, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_agroup_channel(gla2DDrawInfo *di, ActKeysInc *aki, bActionGroup *agrp, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + agroup_to_keylist(agrp, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_action_channel(gla2DDrawInfo *di, ActKeysInc *aki, bAction *act, float ypos) +{ + ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; + + action_to_keylist(act, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + + BLI_freelistN(&keys); + BLI_freelistN(&blocks); +} + +void draw_gpl_channel(gla2DDrawInfo *di, ActKeysInc *aki, bGPDlayer *gpl, float ypos) +{ + ListBase keys = {0, 0}; + + gpl_to_keylist(gpl, &keys, NULL, aki); + draw_keylist(di, &keys, NULL, ypos); + BLI_freelistN(&keys); +} + +/* --------------- Conversion: data -> keyframe list ------------------ */ + +void ob_to_keylist(Object *ob, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bConstraintChannel *conchan; + Key *key= ob_get_key(ob); + + if (ob) { + bDopeSheet *ads= (aki)? (aki->ads) : NULL; + int filterflag; + + /* get filterflag */ + if (ads) + filterflag= ads->filterflag; + else if ((aki) && (aki->actmode == -1)) /* only set like this by NLA */ + filterflag= ADS_FILTER_NLADUMMY; + else + filterflag= 0; + + /* Add object keyframes */ + if ((ob->ipo) && !(filterflag & ADS_FILTER_NOIPOS)) + ipo_to_keylist(ob->ipo, keys, blocks, aki); + + /* Add action keyframes */ + if ((ob->action) && !(filterflag & ADS_FILTER_NOACTS)) + action_nlascaled_to_keylist(ob, ob->action, keys, blocks, aki); + + /* Add shapekey keyframes (only if dopesheet allows, if it is available) */ + if ((key && key->ipo) && !(filterflag & ADS_FILTER_NOSHAPEKEYS)) + ipo_to_keylist(key->ipo, keys, blocks, aki); + + /* Add material keyframes (only if dopesheet allows, if it is available) */ + if ((ob->totcol) && !(filterflag & ADS_FILTER_NOMAT)) { + short a; + + for (a=0; a<ob->totcol; a++) { + Material *ma= give_current_material(ob, a); + + if (ELEM(NULL, ma, ma->ipo) == 0) + ipo_to_keylist(ma->ipo, keys, blocks, aki); + } + } + + /* Add object data keyframes */ + switch (ob->type) { + case OB_CAMERA: /* ------- Camera ------------ */ + { + Camera *ca= (Camera *)ob->data; + if ((ca->ipo) && !(ads->filterflag & ADS_FILTER_NOCAM)) + ipo_to_keylist(ca->ipo, keys, blocks, aki); + } + break; + case OB_LAMP: /* ---------- Lamp ----------- */ + { + Lamp *la= (Lamp *)ob->data; + if ((la->ipo) && !(ads->filterflag & ADS_FILTER_NOLAM)) + ipo_to_keylist(la->ipo, keys, blocks, aki); + } + break; + case OB_CURVE: /* ------- Curve ---------- */ + { + Curve *cu= (Curve *)ob->data; + if ((cu->ipo) && !(ads->filterflag & ADS_FILTER_NOCUR)) + ipo_to_keylist(cu->ipo, keys, blocks, aki); + } + break; + } + + /* Add constraint keyframes */ + if (!(filterflag & ADS_FILTER_NOCONSTRAINTS)) { + for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) { + if (conchan->ipo) + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + } + } + } +} + +static short bezt_in_aki_range (ActKeysInc *aki, BezTriple *bezt) +{ + /* when aki == NULL, we don't care about range */ + if (aki == NULL) + return 1; + + /* if start and end are both 0, then don't care about range */ + if (IS_EQ(aki->start, 0) && IS_EQ(aki->end, 0)) + return 1; + + /* if nla-scaling is in effect, apply appropriate scaling adjustments */ + if (aki->ob) { + float frame= get_action_frame_inv(aki->ob, bezt->vec[1][0]); + return IN_RANGE(frame, aki->start, aki->end); + } + else { + /* check if in range */ + return IN_RANGE(bezt->vec[1][0], aki->start, aki->end); + } +} + +void icu_to_keylist(IpoCurve *icu, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + BezTriple *bezt; + ActKeyColumn *ak, *ak2; + ActKeyBlock *ab, *ab2; + int v; + + if (icu && icu->totvert) { + /* loop through beztriples, making ActKeys and ActKeyBlocks */ + bezt= icu->bezt; + + for (v=0; v<icu->totvert; v++, bezt++) { + /* only if keyframe is in range (optimisation) */ + if (bezt_in_aki_range(aki, bezt)) { + add_bezt_to_keycolumnslist(keys, bezt); + if (blocks) add_bezt_to_keyblockslist(blocks, icu, v); + } + } + + /* update the number of curves that elements have appeared in */ + if (keys) { + for (ak=keys->first, ak2=keys->last; ak && ak2; ak=ak->next, ak2=ak2->prev) { + if (ak->modified) { + ak->modified = 0; + ak->totcurve += 1; + } + + if (ak == ak2) + break; + + if (ak2->modified) { + ak2->modified = 0; + ak2->totcurve += 1; + } + } + } + if (blocks) { + for (ab=blocks->first, ab2=blocks->last; ab && ab2; ab=ab->next, ab2=ab2->prev) { + if (ab->modified) { + ab->modified = 0; + ab->totcurve += 1; + } + + if (ab == ab2) + break; + + if (ab2->modified) { + ab2->modified = 0; + ab2->totcurve += 1; + } + } + } + } +} + +void ipo_to_keylist(Ipo *ipo, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + IpoCurve *icu; + + if (ipo) { + for (icu= ipo->curve.first; icu; icu= icu->next) + icu_to_keylist(icu, keys, blocks, aki); + } +} + +void agroup_to_keylist(bActionGroup *agrp, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + + if (agrp) { + /* loop through action channels */ + for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) { + if (VISIBLE_ACHAN(achan)) { + /* firstly, add keys from action channel's ipo block */ + if (achan->ipo) + ipo_to_keylist(achan->ipo, keys, blocks, aki); + + /* then, add keys from constraint channels */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + } + } + } + } +} + +void action_to_keylist(bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + + if (act) { + /* loop through action channels */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* firstly, add keys from action channel's ipo block */ + if (achan->ipo) + ipo_to_keylist(achan->ipo, keys, blocks, aki); + + /* then, add keys from constraint channels */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + } + } + } +} + +void action_nlascaled_to_keylist(Object *ob, bAction *act, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + Object *oldob= NULL; + + /* although apply and clearing NLA-scaling pre-post creating keylist does impact on performance, + * the effects should be fairly minimal, as we're already going through the keyframes multiple times + * already for blocks too... + */ + if (act) { + /* if 'aki' is provided, store it's current ob to restore later as it might not be the same */ + if (aki) { + oldob= aki->ob; + aki->ob= ob; + } + + /* loop through action channels */ + for (achan= act->chanbase.first; achan; achan= achan->next) { + /* firstly, add keys from action channel's ipo block + * - scaling correction only does times for center-points, so should be faster + */ + if (achan->ipo) { + //actstrip_map_ipo_keys(ob, achan->ipo, 0, 1); // XXX + ipo_to_keylist(achan->ipo, keys, blocks, aki); + //actstrip_map_ipo_keys(ob, achan->ipo, 1, 1); // XXX + } + + /* then, add keys from constraint channels + * - scaling correction only does times for center-points, so should be faster + */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) { + //actstrip_map_ipo_keys(ob, conchan->ipo, 0, 1); // XXX + ipo_to_keylist(conchan->ipo, keys, blocks, aki); + //actstrip_map_ipo_keys(ob, conchan->ipo, 1, 1); // XXX + } + } + } + + /* if 'aki' is provided, restore ob */ + if (aki) + aki->ob= oldob; + } +} + +void gpl_to_keylist(bGPDlayer *gpl, ListBase *keys, ListBase *blocks, ActKeysInc *aki) +{ + bGPDframe *gpf; + ActKeyColumn *ak; + + if (gpl && keys) { + /* loop over frames, converting directly to 'keyframes' (should be in order too) */ + for (gpf= gpl->frames.first; gpf; gpf= gpf->next) { + ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); + BLI_addtail(keys, ak); + + ak->cfra= (float)gpf->framenum; + ak->modified = 1; + ak->handle_type= 0; + + if (gpf->flag & GP_FRAME_SELECT) + ak->sel = SELECT; + else + ak->sel = 0; + } + } +} + |