Index: source/blender/src/drawaction.c =================================================================== --- source/blender/src/drawaction.c (revision 13600) +++ source/blender/src/drawaction.c (working copy) @@ -35,7 +35,10 @@ #include #include +#include +#include + #ifdef HAVE_CONFIG_H #include #endif @@ -1194,6 +1197,62 @@ abn->modified = 1; } +static ActKeysInc *init_aki_data() +{ + static ActKeysInc aki; + + /* init data of static struct here */ + if ((curarea->spacetype == SPACE_ACTION) && NLA_ACTION_SCALED) + aki.ob= OBACT; + else if (curarea->spacetype == SPACE_NLA) + aki.ob= NULL; // FIXME + else + aki.ob= NULL; + + aki.start= G.v2d->cur.xmin - 10; + aki.end= G.v2d->cur.xmax + 10; + + /* only pass pointer for Action Editor if enabled (for now) */ + if ((curarea->spacetype == SPACE_ACTION) && (G.saction->flag & SACTION_HORIZOPTIMISEON)) + return &aki; + else + return NULL; +} + +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 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 draw_object_channel(gla2DDrawInfo *di, Object *ob, float ypos) +{ + bConstraintChannel *conchan; + + if (ob) { + /* draw object keyframes */ + if (ob->ipo) + draw_ipo_channel(di, ob->ipo, ypos); + + /* draw constraint keyframes */ + for (conchan=ob->constraintChannels.first; conchan; conchan=conchan->next) { + if (conchan->ipo) + draw_ipo_channel(di, conchan->ipo, ypos); + } + } +} + /* helper function - find actkeycolumn that occurs on cframe */ static ActKeyColumn *cfra_find_actkeycolumn (ListBase *keys, float cframe) { @@ -1210,6 +1269,7 @@ return NULL; } + /* 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) { @@ -1304,87 +1364,470 @@ } -static ActKeysInc *init_aki_data() +void insertsort_icu_keys(BezTriple* bezt, int totvert) { - static ActKeysInc aki; + int i, j; + BezTriple tmp; - /* init data of static struct here */ - if ((curarea->spacetype == SPACE_ACTION) && NLA_ACTION_SCALED) - aki.ob= OBACT; - else if (curarea->spacetype == SPACE_NLA) - aki.ob= NULL; // FIXME - else - aki.ob= NULL; + if (!bezt) { + return; + } + + for (i = 1; i < totvert; ++i) { + for (j = i - 1; j >= 0; --j) { + if (bezt[j + 1].vec[1][0] < bezt[j].vec[1][0]) { + tmp = bezt[j]; + bezt[j] = bezt[j + 1]; + bezt[j + 1] = tmp; + } else { + break; + } + } + } +} + +void insertsort_ipo_keys(Ipo* ipo) +{ + IpoCurve* cu; + + if (!ipo) + return; + + for (cu = ipo->curve.first; cu; cu = cu->next) { + insertsort_icu_keys(cu->bezt, cu->totvert); + } +} + +/* find the key immediately before time t using a binary search */ +BezTriple* find_key(IpoCurve* icu, BezTriple* bezt, float t) +{ + int lbound = bezt - icu->bezt; + int ubound = icu->totvert; + int idx; + int diff; + + if (!bezt) { + return NULL; + } + + /* handle common trivial scenarios */ + if (bezt->vec[1][0] > t) { + return NULL; + } + if (t > icu->bezt[icu->totvert - 1].vec[1][0]) { + return &icu->bezt[icu->totvert - 1]; + } + + while (ubound - lbound > 1) { + diff = (ubound - lbound) / 2; + idx = lbound + diff; - aki.start= G.v2d->cur.xmin - 10; - aki.end= G.v2d->cur.xmax + 10; + if (icu->bezt[idx].vec[1][0] < t) { + lbound += diff; + } else { + ubound = lbound + diff; + } + } - /* only pass pointer for Action Editor if enabled (for now) */ - if ((curarea->spacetype == SPACE_ACTION) && (G.saction->flag & SACTION_HORIZOPTIMISEON)) - return &aki; - else - return NULL; + return &icu->bezt[lbound]; } -void draw_object_channel(gla2DDrawInfo *di, Object *ob, float ypos) +void find_unculled_key_range(IpoCurve *icu, ActKeysInc *aki, BezTriple **start, BezTriple **end) { - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - ActKeysInc *aki = init_aki_data(); + if (!icu || icu->totvert == 0) { + *start = *end = NULL; + return; + } + + if (!aki) { + *start = icu->bezt; + *end = &icu->bezt[icu->totvert - 1]; + return; + } + + if (aki->ob) { + *start = find_key(icu, icu->bezt, get_action_frame(aki->ob, aki->start)); + *end = find_key(icu, icu->bezt, get_action_frame(aki->ob, aki->end)); + } else { + *start = find_key(icu, icu->bezt, aki->start); + *end = find_key(icu, icu->bezt, aki->end); + } + + if (!(*start)) { + *start = icu->bezt; + } +} - ob_to_keylist(ob, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); +/* batch draw all of the keyframes in an ipo curve using GL_QUADS */ +void draw_icu_keyframes(gla2DDrawInfo *di, ActKeysInc *aki, IpoCurve *icu, float ypos, int selected) +{ + BezTriple *bezt; + BezTriple *start, *end; + int sc_x, sc_y; + int last_sc_x = -1; + const int keysize = 4; + const int bordsize = keysize + 1; + + if (!icu || !icu->totvert) { + return; + } - BLI_freelistN(&keys); - BLI_freelistN(&blocks); + /* find the first visible start key and the last visible end key */ + find_unculled_key_range(icu, aki, &start, &end); + + gla2DDrawTranslatePt(di, start->vec[1][0], ypos, &sc_x, &sc_y); + last_sc_x = sc_x - 3; + + /* for efficiency we need to batch draw the all the keyframe borders first */ + glColor3ub(0, 0, 0); + glBegin(GL_QUADS); + for (bezt=start; bezt <= end; ++bezt) { + if(BEZSELECTED(bezt) == selected) { + gla2DDrawTranslatePt(di, bezt->vec[1][0], ypos, &sc_x, &sc_y); + + /* avoid redrawing over the same pixels */ + if (abs(sc_x - last_sc_x) > 2) { + glVertex2i(sc_x, sc_y - bordsize); + glVertex2i(sc_x + bordsize, sc_y); + glVertex2i(sc_x, sc_y + bordsize); + glVertex2i(sc_x - bordsize, sc_y); + last_sc_x = sc_x; + } + } + } + glEnd(); + + gla2DDrawTranslatePt(di, start->vec[1][0], ypos, &sc_x, &sc_y); + last_sc_x = sc_x - 3; + + if (selected) { + glColor3ub(0xF1, 0xCA, 0x13); + } else { + glColor3ub(0xE9, 0xE9, 0xE9); + } + + /* draw the yellow keyframe cores */ + glBegin(GL_QUADS); + for (bezt=start; bezt <= end; ++bezt) { + if(BEZSELECTED(bezt) == selected) { + gla2DDrawTranslatePt(di, bezt->vec[1][0], ypos, &sc_x, &sc_y); + + if (abs(sc_x - last_sc_x) > 2) { + glVertex2i(sc_x, sc_y - keysize); + glVertex2i(sc_x + keysize, sc_y); + glVertex2i(sc_x, sc_y + keysize); + glVertex2i(sc_x - keysize, sc_y); + last_sc_x = sc_x; + } + } + } + glEnd(); } -void draw_ipo_channel(gla2DDrawInfo *di, Ipo *ipo, float ypos) +void draw_block(gla2DDrawInfo *di, float tstart, float tend, float ypos, int selected) { - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; - ActKeysInc *aki = init_aki_data(); + int sc_xa, sc_ya; + int sc_xb, sc_yb; + const int halfheight = 3; + + gla2DDrawTranslatePt(di, tstart, ypos, &sc_xa, &sc_ya); + gla2DDrawTranslatePt(di, tend, ypos, &sc_xb, &sc_yb); + + if (selected) { + BIF_ThemeColor4(TH_STRIP_SELECT); + } else { + BIF_ThemeColor4(TH_STRIP); + } + + if (sc_xa - sc_xb < 1) { + glRectf(sc_xa + 1, sc_ya - halfheight, sc_xb - 1, sc_yb + halfheight); + } +} - ipo_to_keylist(ipo, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); +/* find the next key block starting at or after cur */ +void find_next_keyblock( + IpoCurve *icu, BezTriple *cur, BezTriple **kbstart, BezTriple **kbend, int *selected + ) +{ + int v; + *selected = 0; - BLI_freelistN(&keys); - BLI_freelistN(&blocks); + if (cur == NULL) { + *kbstart = *kbend = NULL; + return; + } + + if (cur - icu->bezt >= icu->totvert) { + *kbstart = *kbend = NULL; + return; + } + + for (v = cur - icu->bezt; v < icu->totvert - 1; v++) { + *kbstart = &(icu->bezt[v]); + *kbend = *kbstart + 1; + + if ( + IS_EQ((*kbend)->vec[1][1], (*kbstart)->vec[1][1]) && + IS_EQ((*kbend)->vec[1][1], (*kbend)->vec[0][1]) && + IS_EQ((*kbstart)->vec[1][1], (*kbstart)->vec[2][1]) + ) { + if (selected && (BEZSELECTED(*kbstart) || BEZSELECTED(*kbend))) { + *selected = 1; + } + + return; + } + } + + *kbstart = *kbend = NULL; } +/* find a keyblock starting on or after cur which begins at start and ends at end */ +int find_keyblock_time(IpoCurve *icu, BezTriple *cur, float start, float end, int *selected) +{ + BezTriple *kbstart, *kbend; + + if (cur == NULL) { + return 0; + } + + do { + find_next_keyblock(icu, cur, &kbstart, &kbend, selected); + + if (kbstart == NULL || kbend == NULL) { /* no block exists */ + return 0; + } else if (kbstart->vec[1][0] > start || kbend->vec[1][0] > end) { /* no aligned block */ + return 0; + } else if (IS_EQ(kbstart->vec[1][0], start) && IS_EQ(kbend->vec[1][0], end)) { /* block exists */ + return 1; + } + + cur = kbend; + } while (1); + + return 0; +} + +void draw_ipo_keyblocks(gla2DDrawInfo *di, ActKeysInc *aki, Ipo* ipo, float ypos) +{ + BezTriple **acur; /* array of BezTriple* to help speedup drawing */ + BezTriple **bezcopy, **bezorig; /* sorted copy and original keyframes */ + + IpoCurve *mincu; + BezTriple *minbeztcur, *minbeztstart, *minbeztend; + int minvert; + + IpoCurve *cu; + BezTriple *kbstart, *kbend; + + int selected; + int ncu; + int v; + + if (!ipo->curve.first) { + return; + } + + ncu = 0; + for (cu = ipo->curve.first; cu; cu = cu->next) { + ++ncu; + } + + acur = MEM_mallocN(ncu * sizeof(BezTriple*), "draw_ipo_keyblocks"); + bezcopy = MEM_mallocN(ncu * sizeof(BezTriple*), "draw_ipo_keyblocks"); + bezorig = MEM_mallocN(ncu * sizeof(BezTriple*), "draw_ipo_keyblocks"); + + /* create copies of each ipo curve for sorting */ + v = 0; + for (cu = ipo->curve.first; cu; cu = cu->next, ++v) { + bezcopy[v] = (BezTriple*)MEM_mallocN(cu->totvert * sizeof(BezTriple), "draw_ipo_keyblocks"); + memcpy((void*)bezcopy[v], (void*)cu->bezt, cu->totvert * sizeof(BezTriple)); + bezorig[v] = cu->bezt; + cu->bezt = bezcopy[v]; + + acur[v] = cu->bezt; + } + + /* insertion sort the keys (the keys should be already sorted, for the most part) */ + insertsort_ipo_keys(ipo); + + /* find the curve with the minimum number of keyframes for efficiency */ + cu = ipo->curve.first; + minvert = cu->totvert; + mincu = cu; + for (cu = cu->next; cu; cu = cu->next) { + if (cu->totvert < minvert) { + minvert = cu->totvert; + mincu = cu; + } + } + + find_unculled_key_range(mincu, aki, &minbeztstart, &minbeztend); + minbeztcur = minbeztstart; + do { + int hasblock = 1; + find_next_keyblock(mincu, minbeztcur, &kbstart, &kbend, &selected); + + /* no keyblock at or past cur exists */ + if (kbstart == NULL || kbend == NULL) { + break; + } + + /* the beginning of the next keyblock is outside of our visible range */ + if (kbstart->vec[1][0] > minbeztend->vec[1][0]) { + break; + } + + /* check if all other IPO curves have a block that begins and ends at the current block */ + v = 0; + for (cu = ipo->curve.first; cu; cu = cu->next, ++v) { + + /* the last block for one of the curves has been exceeded (no further blocks will match up) */ + if (!acur[v]) { + //hasblock = 0; + break; + } + + /* set the keyframe to continue searching from to the one right before our block */ + acur[v] = find_key(cu, acur[v], kbstart->vec[1][0]); + + /* see if we can find the block */ + hasblock = find_keyblock_time(cu, acur[v], kbstart->vec[1][0], kbend->vec[1][0], &selected); + + /* one curve does not match, so we will not draw a long keyframe */ + if (!hasblock) { + break; + } + } + + if (!acur[v]) { + break; + } + + if (hasblock) { + draw_block(di, kbstart->vec[1][0], kbend->vec[1][0], ypos, selected); + } + + minbeztcur = kbend; + } while (1); + + + /* cleanup */ + v = 0; + for (cu = ipo->curve.first; cu; cu = cu->next, ++v) { + cu->bezt = bezorig[v]; + MEM_freeN(bezcopy[v]); + } + MEM_freeN(bezcopy); + MEM_freeN(bezorig); + MEM_freeN(acur); +} + +void draw_icu_keyblocks(gla2DDrawInfo *di, ActKeysInc *aki, IpoCurve* icu, float ypos) +{ + BezTriple *kbstart, *kbend; /* keyblock start and end */ + BezTriple *culregstart, *culregend; /* region of keys not culled */ + BezTriple *bezcopy, *bezorig; /* sorted copy and original IpoCurve keys */ + + BezTriple *cur; + int selected; + + if (!icu->bezt) { + return; + } + + /* make a sorted copy of the bezier triples to make drawing quick and easy */ + bezcopy = MEM_mallocN(sizeof(BezTriple) * icu->totvert, "draw_icu_keyblocks"); + memcpy(bezcopy, icu->bezt, sizeof(BezTriple) * icu->totvert); + bezorig = icu->bezt; + icu->bezt = bezcopy; + + /* sort */ + insertsort_icu_keys(bezcopy, icu->totvert); + + /* find the visible region of keys */ + find_unculled_key_range(icu, aki, &culregstart, &culregend); + cur = culregstart; + + find_next_keyblock(icu, cur, &kbstart, &kbend, &selected); + cur = kbend; + while (kbstart != NULL && kbend != NULL) { + draw_block(di, kbstart->vec[1][0], kbend->vec[1][0], ypos, selected); + + if (kbstart->vec[1][0] > culregend->vec[1][0]) { + break; + } + + find_next_keyblock(icu, cur, &kbstart, &kbend, &selected); + cur = kbend; + } + + /* cleanup */ + icu->bezt = bezorig; + MEM_freeN(bezcopy); +} + void draw_icu_channel(gla2DDrawInfo *di, IpoCurve *icu, float ypos) { - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; ActKeysInc *aki = init_aki_data(); - icu_to_keylist(icu, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - - BLI_freelistN(&keys); - BLI_freelistN(&blocks); + draw_icu_keyblocks(di, aki, icu, ypos); + draw_icu_keyframes(di, aki, icu, ypos, 0); + draw_icu_keyframes(di, aki, icu, ypos, 1); } -void draw_agroup_channel(gla2DDrawInfo *di, bActionGroup *agrp, float ypos) +void draw_ipo_channel(gla2DDrawInfo *di, Ipo *ipo, float ypos) { - ListBase keys = {0, 0}; - ListBase blocks = {0, 0}; + IpoCurve *icu; ActKeysInc *aki = init_aki_data(); + + if (ipo) { + /* draw blocks */ + draw_ipo_keyblocks(di, aki, ipo, ypos); + + /* draw unselected */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + draw_icu_keyframes(di, aki, icu, ypos, 0); + } + /* draw selected */ + for (icu= ipo->curve.first; icu; icu= icu->next) { + draw_icu_keyframes(di, aki, icu, ypos, 1); + } + } +} - agroup_to_keylist(agrp, &keys, &blocks, aki); - draw_keylist(di, &keys, &blocks, ypos); - BLI_freelistN(&keys); - BLI_freelistN(&blocks); +void draw_agroup_channel(gla2DDrawInfo *di, bActionGroup *agrp, float ypos) +{ + bActionChannel *achan; + bConstraintChannel *conchan; + + if (agrp) { + /* loop through action channels */ + for (achan= agrp->channels.first; achan && achan->grp==agrp; achan= achan->next) { + /* firstly, add keys from action channel's ipo block */ + if (achan->ipo) + draw_ipo_channel(di, achan->ipo, ypos); + + /* then, add keys from constraint channels */ + for (conchan= achan->constraintChannels.first; conchan; conchan= conchan->next) { + if (conchan->ipo) + draw_ipo_channel(di, conchan->ipo, ypos); + } + } + } } void draw_action_channel(gla2DDrawInfo *di, bAction *act, float ypos) { ListBase keys = {0, 0}; + ListBase blocks = {0, 0}; ActKeysInc *aki = init_aki_data(); - action_to_keylist(act, &keys, NULL, aki); - draw_keylist(di, &keys, NULL, ypos); + action_to_keylist(act, &keys, &blocks, aki); + draw_keylist(di, &keys, &blocks, ypos); + BLI_freelistN(&keys); + BLI_freelistN(&blocks); } /* --------------- Conversion: data -> keyframe list ------------------ */ @@ -1409,23 +1852,6 @@ } } -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 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;