/** * $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) 2009 Blender Foundation, Joshua Leung * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Joshua Leung (full recode) * * ***** END GPL LICENSE BLOCK ***** */ /* System includes ----------------------------------------------------- */ #include #include #include #include #ifdef HAVE_CONFIG_H #include #endif #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "BLI_dlrbTree.h" #include "DNA_listBase.h" #include "DNA_anim_types.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_camera_types.h" #include "DNA_curve_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 "DNA_world_types.h" #include "DNA_view2d_types.h" #include "BKE_action.h" #include "BKE_depsgraph.h" #include "BKE_fcurve.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" #include "BIF_gl.h" #include "BIF_glutil.h" #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.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" /* *************************** Keyframe Processing *************************** */ /* Create a ActKeyColumn from a BezTriple */ static ActKeyColumn *bezt_to_new_actkeycolumn(BezTriple *bezt) { ActKeyColumn *ak= MEM_callocN(sizeof(ActKeyColumn), "ActKeyColumn"); /* store settings based on state of BezTriple */ ak->cfra= bezt->vec[1][0]; ak->sel= BEZSELECTED(bezt) ? SELECT : 0; // TODO: handle type = bezt->h1 or bezt->h2 ak->handle_type= 0; /* set 'modified', since this is used to identify long keyframes */ ak->modified = 1; return ak; } /* Add the given BezTriple to the given 'list' of Keyframes */ static void add_bezt_to_keycolumns_list(DLRBT_Tree *keys, BezTriple *bezt) { ActKeyColumn *new_ak=NULL; if ELEM(NULL, keys, bezt) return; /* if there are no keys already, just add as root */ if (keys->root == NULL) { /* just add this as the root, then call the tree-balancing functions to validate */ new_ak= bezt_to_new_actkeycolumn(bezt); keys->root= (DLRBT_Node *)new_ak; } else { ActKeyColumn *ak, *akp=NULL, *akn=NULL; /* traverse tree to find an existing entry to update the status of, * or a suitable point to add at */ for (ak= keys->root; ak; akp= ak, ak= akn) { /* check if this is a match, or whether we go left or right */ if (ak->cfra == bezt->vec[1][0]) { /* set selection status and 'touched' status */ if (BEZSELECTED(bezt)) ak->sel = SELECT; ak->modified += 1; /* done... no need to insert */ return; } else { ActKeyColumn **aknp= NULL; /* check if go left or right, but if not available, add new node */ if (ak->cfra < bezt->vec[1][0]) aknp= &ak->right; else aknp= &ak->left; /* if this does not exist, add a new node, otherwise continue... */ if (*aknp == NULL) { /* add a new node representing this, and attach it to the relevant place */ new_ak= bezt_to_new_actkeycolumn(bezt); new_ak->parent= ak; *aknp= new_ak; break; } else akn= *aknp; } } } /* now, balance the tree taking into account this newly added node */ BLI_dlrbTree_insert(keys, (DLRBT_Node *)new_ak); } /* Create a ActKeyColumn for a pair of BezTriples */ static ActKeyBlock *bezts_to_new_actkeyblock(BezTriple *prev, BezTriple *beztn) { ActKeyBlock *ab= MEM_callocN(sizeof(ActKeyBlock), "ActKeyBlock"); ab->start= prev->vec[1][0]; ab->end= beztn->vec[1][0]; ab->val= beztn->vec[1][1]; ab->sel= (BEZSELECTED(prev) || BEZSELECTED(beztn)) ? SELECT : 0; ab->modified = 1; return ab; } static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, FCurve *fcu, int index) { ActKeyBlock *new_ab= NULL; BezTriple *beztn=NULL, *prev=NULL; BezTriple *bezt; int v; /* get beztriples */ beztn= (fcu->bezt + index); /* we need to go through all beztriples, as they may not be in order (i.e. during transform) */ // TODO: this seems to be a bit of a bottleneck for (v=0, bezt=fcu->bezt; v < fcu->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; /* if there are no blocks already, just add as root */ if (blocks->root == NULL) { /* just add this as the root, then call the tree-balancing functions to validate */ new_ab= bezts_to_new_actkeyblock(prev, beztn); blocks->root= (DLRBT_Node *)new_ab; } else { ActKeyBlock *ab, *abp=NULL, *abn=NULL; /* try to find a keyblock that starts on the previous beztriple, and add a new one if none start there * 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->root; ab; abp= ab, ab= abn) { /* check if this is a match, or whether we go left or right */ if (ab->start == prev->vec[1][0]) { /* set selection status and 'touched' status */ if (BEZSELECTED(beztn)) ab->sel = SELECT; ab->modified += 1; /* done... no need to insert */ return; } else { ActKeyBlock **abnp= NULL; /* check if go left or right, but if not available, add new node */ if (ab->start < prev->vec[1][0]) abnp= &ab->right; else abnp= &ab->left; /* if this does not exist, add a new node, otherwise continue... */ if (*abnp == NULL) { /* add a new node representing this, and attach it to the relevant place */ new_ab= bezts_to_new_actkeyblock(prev, beztn); new_ab->parent= ab; *abnp= new_ab; break; } else abn= *abnp; } } } /* now, balance the tree taking into account this newly added node */ BLI_dlrbTree_insert(blocks, (DLRBT_Node *)new_ab); } /* --------- */ /* Handle the 'touched' status of ActKeyColumn tree nodes */ static void set_touched_actkeycolumn (ActKeyColumn *ak) { /* sanity check */ if (ak == NULL) return; /* deal with self first */ if (ak->modified) { ak->modified= 0; ak->totcurve++; } /* children */ set_touched_actkeycolumn(ak->left); set_touched_actkeycolumn(ak->right); } /* Handle the 'touched' status of ActKeyBlock tree nodes */ static void set_touched_actkeyblock (ActKeyBlock *ab) { /* sanity check */ if (ab == NULL) return; /* deal with self first */ if (ab->modified) { ab->modified= 0; ab->totcurve++; } /* children */ set_touched_actkeyblock(ab->left); set_touched_actkeyblock(ab->right); } /* *************************** Keyframe Drawing *************************** */ /* helper function - find actkeycolumn that occurs on cframe */ static ActKeyColumn *cfra_find_actkeycolumn (ActKeyColumn *ak, float cframe) { /* sanity checks */ if (ak == NULL) return NULL; /* check if this is a match, or whether it is in some subtree */ if (cframe < ak->cfra) return cfra_find_actkeycolumn(ak->left, cframe); else if (cframe > ak->cfra) return cfra_find_actkeycolumn(ak->right, cframe); else return ak; /* match */ } /* -------- */ /* coordinates for diamond shape */ static const float _unit_diamond_shape[4][2] = { {0.0f, 1.0f}, /* top vert */ {1.0f, 0.0f}, /* mid-right */ {0.0f, -1.0f}, /* bottom vert */ {-1.0f, 0.0f} /* mid-left */ }; /* draw a simple diamond shape with OpenGL */ void draw_keyframe_shape (float x, float y, float xscale, float hsize, short sel, short mode) { static GLuint displist1=0; static GLuint displist2=0; /* initialise 2 display lists for diamond shape - one empty, one filled */ if (displist1 == 0) { displist1= glGenLists(1); glNewList(displist1, GL_COMPILE); glBegin(GL_LINE_LOOP); glVertex2fv(_unit_diamond_shape[0]); glVertex2fv(_unit_diamond_shape[1]); glVertex2fv(_unit_diamond_shape[2]); glVertex2fv(_unit_diamond_shape[3]); glEnd(); glEndList(); } if (displist2 == 0) { displist2= glGenLists(1); glNewList(displist2, GL_COMPILE); glBegin(GL_QUADS); glVertex2fv(_unit_diamond_shape[0]); glVertex2fv(_unit_diamond_shape[1]); glVertex2fv(_unit_diamond_shape[2]); glVertex2fv(_unit_diamond_shape[3]); glEnd(); glEndList(); } /* adjust view transform before starting */ glTranslatef(x, y, 0.0f); glScalef(1.0f/xscale*hsize, hsize, 1.0f); /* anti-aliased lines for more consistent appearance */ glEnable(GL_LINE_SMOOTH); /* draw! */ if ELEM(mode, KEYFRAME_SHAPE_INSIDE, KEYFRAME_SHAPE_BOTH) { /* interior - hardcoded colors (for selected and unselected only) */ if (sel) UI_ThemeColorShade(TH_STRIP_SELECT, 50); else glColor3ub(0xE9, 0xE9, 0xE9); glCallList(displist2); } if ELEM(mode, KEYFRAME_SHAPE_FRAME, KEYFRAME_SHAPE_BOTH) { /* exterior - black frame */ glColor3ub(0, 0, 0); glCallList(displist1); } glDisable(GL_LINE_SMOOTH); /* restore view transform */ glScalef(xscale/hsize, 1.0f/hsize, 1.0); glTranslatef(-x, -y, 0.0f); } static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, float ypos) { ActKeyColumn *ak; ActKeyBlock *ab; float xscale; glEnable(GL_BLEND); /* get View2D scaling factor */ UI_view2d_getscale(v2d, &xscale, NULL); /* 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((ActKeyColumn *)keys->root, ab->start); startCurves = (ak)? ak->totcurve: 0; ak= cfra_find_actkeycolumn((ActKeyColumn *)keys->root, 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) { /* draw block */ if (ab->sel) UI_ThemeColor4(TH_STRIP_SELECT); else UI_ThemeColor4(TH_STRIP); glRectf(ab->start, ypos-5, ab->end, ypos+5); } } } /* draw keys */ if (keys) { for (ak= keys->first; ak; ak= ak->next) { /* optimisation: if keyframe doesn't appear within 5 units (screenspace) in visible area, don't draw * - this might give some improvements, since we current have to flip between view/region matrices */ if (IN_RANGE_INCL(ak->cfra, v2d->cur.xmin, v2d->cur.xmax) == 0) continue; /* draw using OpenGL - uglier but faster */ // NOTE1: a previous version of this didn't work nice for some intel cards // NOTE2: if we wanted to go back to icons, these are icon = (ak->sel & SELECT) ? ICON_SPACE2 : ICON_SPACE3; draw_keyframe_shape(ak->cfra, ypos, xscale, 5.0f, (ak->sel & SELECT), KEYFRAME_SHAPE_BOTH); } } glDisable(GL_BLEND); } /* *************************** Channel Drawing Funcs *************************** */ void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos) { DLRBT_Tree keys, blocks; BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); scene_to_keylist(ads, sce, &keys, &blocks); BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); draw_keylist(v2d, &keys, &blocks, ypos); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos) { DLRBT_Tree keys, blocks; BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); ob_to_keylist(ads, ob, &keys, &blocks); BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); draw_keylist(v2d, &keys, &blocks, ypos); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos) { DLRBT_Tree keys, blocks; BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); fcurve_to_keylist(adt, fcu, &keys, &blocks); BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); draw_keylist(v2d, &keys, &blocks, ypos); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos) { DLRBT_Tree keys, blocks; BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); agroup_to_keylist(adt, agrp, &keys, &blocks); BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); draw_keylist(v2d, &keys, &blocks, ypos); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos) { DLRBT_Tree keys, blocks; BLI_dlrbTree_init(&keys); BLI_dlrbTree_init(&blocks); action_to_keylist(adt, act, &keys, &blocks); BLI_dlrbTree_linkedlist_sync(&keys); BLI_dlrbTree_linkedlist_sync(&blocks); draw_keylist(v2d, &keys, &blocks, ypos); BLI_dlrbTree_free(&keys); BLI_dlrbTree_free(&blocks); } void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos) { DLRBT_Tree keys; BLI_dlrbTree_init(&keys); gpl_to_keylist(ads, gpl, &keys, NULL); BLI_dlrbTree_linkedlist_sync(&keys); draw_keylist(v2d, &keys, NULL, ypos); BLI_dlrbTree_free(&keys); } /* *************************** Keyframe List Conversions *************************** */ void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, DLRBT_Tree *blocks) { if (sce) { AnimData *adt; int filterflag; /* get filterflag */ if (ads) filterflag= ads->filterflag; else filterflag= 0; /* scene animdata */ if ((sce->adt) && !(filterflag & ADS_FILTER_NOSCE)) { adt= sce->adt; // TODO: when we adapt NLA system, this needs to be the NLA-scaled version if (adt->action) action_to_keylist(adt, adt->action, keys, blocks); } /* world animdata */ if ((sce->world) && (sce->world->adt) && !(filterflag & ADS_FILTER_NOWOR)) { adt= sce->world->adt; // TODO: when we adapt NLA system, this needs to be the NLA-scaled version if (adt->action) action_to_keylist(adt, adt->action, keys, blocks); } } } void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *blocks) { Key *key= ob_get_key(ob); if (ob) { int filterflag; /* get filterflag */ if (ads) filterflag= ads->filterflag; else filterflag= 0; /* Add action keyframes */ if (ob->adt && ob->adt->action) action_to_keylist(ob->adt, ob->adt->action, keys, blocks); /* Add shapekey keyframes (only if dopesheet allows, if it is available) */ if ((key && key->adt && key->adt->action) && !(filterflag & ADS_FILTER_NOSHAPEKEYS)) action_to_keylist(key->adt, key->adt->action, keys, blocks); // TODO: restore materials, and object data, etc. } } void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, DLRBT_Tree *blocks) { BezTriple *bezt; int v; if (fcu && fcu->totvert && fcu->bezt) { /* apply NLA-mapping (if applicable) */ if (adt) ANIM_nla_mapping_apply_fcurve(adt, fcu, 0, 1); /* loop through beztriples, making ActKeys and ActKeyBlocks */ bezt= fcu->bezt; for (v=0; v < fcu->totvert; v++, bezt++) { add_bezt_to_keycolumns_list(keys, bezt); if (blocks) add_bezt_to_keyblocks_list(blocks, fcu, v); } /* update the number of curves that elements have appeared in */ // FIXME: this is broken with the new tree structure for now... if (keys) set_touched_actkeycolumn(keys->root); if (blocks) set_touched_actkeyblock(blocks->root); /* unapply NLA-mapping if applicable */ ANIM_nla_mapping_apply_fcurve(adt, fcu, 1, 1); } } void agroup_to_keylist(AnimData *adt, bActionGroup *agrp, DLRBT_Tree *keys, DLRBT_Tree *blocks) { FCurve *fcu; if (agrp) { /* loop through F-Curves */ for (fcu= agrp->channels.first; fcu && fcu->grp==agrp; fcu= fcu->next) { fcurve_to_keylist(adt, fcu, keys, blocks); } } } void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, DLRBT_Tree *blocks) { FCurve *fcu; if (act) { /* loop through F-Curves */ for (fcu= act->curves.first; fcu; fcu= fcu->next) { fcurve_to_keylist(adt, fcu, keys, blocks); } } } void gpl_to_keylist(bDopeSheet *ads, bGPDlayer *gpl, DLRBT_Tree *keys, DLRBT_Tree *blocks) { 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((ListBase *)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; } } }