Blender V4.3
nla_draw.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors, Joshua Leung. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cfloat>
10#include <cmath>
11#include <cstdio>
12#include <cstdlib>
13#include <cstring>
14
15#include "DNA_anim_types.h"
16#include "DNA_node_types.h"
17#include "DNA_screen_types.h"
18#include "DNA_space_types.h"
19
20#include "BLI_blenlib.h"
21#include "BLI_range.h"
22#include "BLI_utildefines.h"
23
24#include "BLT_translation.hh"
25
26#include "BKE_action.hh"
27#include "BKE_fcurve.hh"
28#include "BKE_nla.hh"
29
30#include "ED_anim_api.hh"
31#include "ED_keyframes_draw.hh"
33
34#include "GPU_immediate.hh"
35#include "GPU_immediate_util.hh"
36#include "GPU_state.hh"
37
38#include "WM_types.hh"
39
40#include "UI_interface.hh"
41#include "UI_resources.hh"
42#include "UI_view2d.hh"
43
44#include "nla_intern.hh" /* own include */
45#include "nla_private.h"
46
47using namespace blender;
48
49/* *********************************************** */
50/* Strips */
51
52/* Action-Line ---------------------- */
53
54void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
55{
56 if (adt && (adt->flag & ADT_NLA_EDIT_ON)) {
57 /* greenish color (same as tweaking strip) */
59 }
60 else {
61 if (act) {
62 /* reddish color - same as dopesheet summary */
64 }
65 else {
66 /* grayish-red color */
68 }
69 }
70
71 /* when an NLA track is tagged "solo", action doesn't contribute,
72 * so shouldn't be as prominent */
73 if (adt && (adt->flag & ADT_NLA_SOLO_TRACK)) {
74 color[3] *= 0.15f;
75 }
76}
77
78/* draw the keyframes in the specified Action */
80 View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax)
81{
82 if (act == nullptr) {
83 return;
84 }
85
86 /* get a list of the keyframes with NLA-scaling applied */
87 AnimKeylist *keylist = ED_keylist_create();
88 action_to_keylist(adt, act, keylist, 0, {v2d->cur.xmin, v2d->cur.xmax});
89
90 if (ED_keylist_is_empty(keylist)) {
91 ED_keylist_free(keylist);
92 return;
93 }
94
95 /* draw a darkened region behind the strips
96 * - get and reset the background color, this time without the alpha to stand out better
97 * (amplified alpha is used instead, but clamped to avoid 100% opacity)
98 */
99 float color[4];
100 nla_action_get_color(adt, act, color);
101 color[3] = min_ff(0.7f, color[3] * 2.5f);
102
105
107
108 immUniformColor4fv(color);
109
110 /* - draw a rect from the first to the last frame (no extra overlaps for now)
111 * that is slightly stumpier than the track background (hardcoded 2-units here)
112 */
113
114 Range2f frame_range;
115 ED_keylist_all_keys_frame_range(keylist, &frame_range);
116 immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2);
118
119 /* Count keys before drawing. */
120 /* NOTE: It's safe to cast #DLRBT_Tree, as it's designed to degrade down to a #ListBase. */
121 const ListBase *keys = ED_keylist_listbase(keylist);
122 uint key_len = BLI_listbase_count(keys);
123
124 if (key_len > 0) {
126 KeyframeShaderBindings sh_bindings;
128 sh_bindings.size_id = GPU_vertformat_attr_add(
129 format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
130 sh_bindings.color_id = GPU_vertformat_attr_add(
133 format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
134 sh_bindings.flags_id = GPU_vertformat_attr_add(
135 format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
136
139 immUniform1f("outline_scale", 1.0f);
140 immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
141 immBegin(GPU_PRIM_POINTS, key_len);
142
143 /* - disregard the selection status of keyframes so they draw a certain way
144 * - size is 6.0f which is smaller than the editable keyframes, so that there is a distinction
145 */
146 LISTBASE_FOREACH (const ActKeyColumn *, ak, keys) {
147 draw_keyframe_shape(ak->cfra,
148 y,
149 6.0f,
150 false,
151 ak->key_type,
153 1.0f,
154 &sh_bindings,
157 }
158
159 immEnd();
162 }
163
164 /* free icons */
165 ED_keylist_free(keylist);
166}
167
168/* Strip Markers ------------------------ */
169
170/* Markers inside an action strip */
172 NlaStrip *strip, float yminc, float ymaxc, int shade, const bool dashed)
173{
174 const bAction *act = strip->act;
175
176 if (ELEM(nullptr, act, act->markers.first)) {
177 return;
178 }
179
180 const uint shdr_pos = GPU_vertformat_attr_add(
182 if (dashed) {
184
185 float viewport_size[4];
186 GPU_viewport_size_get_f(viewport_size);
188 "viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
189
190 immUniform1i("colors_len", 0); /* "simple" mode */
191 immUniform1f("dash_width", 6.0f);
192 immUniform1f("udash_factor", 0.5f);
193 }
194 else {
196 }
198
200 LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
201 if ((marker->frame > strip->actstart) && (marker->frame < strip->actend)) {
202 float frame = nlastrip_get_frame(strip, marker->frame, NLATIME_CONVERT_MAP);
203
204 /* just a simple line for now */
205 /* XXX: draw a triangle instead... */
206 immVertex2f(shdr_pos, frame, yminc + 1);
207 immVertex2f(shdr_pos, frame, ymaxc - 1);
208 }
209 }
210 immEnd();
211
213}
214
215/* Markers inside a NLA-Strip */
216static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
217{
218 GPU_line_width(2.0f);
219
220 if (strip->type == NLASTRIP_TYPE_CLIP) {
221 /* try not to be too conspicuous, while being visible enough when transforming */
222 int shade = (strip->flag & NLASTRIP_FLAG_SELECT) ? -60 : -40;
223
224 /* just draw the markers in this clip */
225 nla_actionclip_draw_markers(strip, yminc, ymaxc, shade, true);
226 }
227 else if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
228 /* just a solid color, so that it is very easy to spot */
229 int shade = 20;
230 /* draw the markers in the first level of strips only (if they are actions) */
231 LISTBASE_FOREACH (NlaStrip *, nls, &strip->strips) {
232 if (nls->type == NLASTRIP_TYPE_CLIP) {
233 nla_actionclip_draw_markers(nls, yminc, ymaxc, shade, false);
234 }
235 }
236 }
237
238 GPU_line_width(1.0f);
239}
240
241/* Strips (Proper) ---------------------- */
242
243/* Get colors for drawing NLA Strips. */
244static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float color[3])
245{
246 const bool is_selected = strip->flag & NLASTRIP_FLAG_SELECT;
247 switch (strip->type) {
249 /* Action Strip. */
250 if (adt && (adt->flag & ADT_NLA_EDIT_ON) && (adt->actstrip == strip)) {
251 /* Active strip tweak - tweak theme is applied only to active edit strip,
252 * not linked-duplicates.
253 */
255 break;
256 }
257
258 if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
259 /* Non-active strip tweak - display warning theme
260 * for non active linked-duplicates.
261 */
263 break;
264 }
265 if (strip->flag & NLASTRIP_FLAG_SELECT) {
266 /* selected. */
268 break;
269 }
270
271 /* unselected - use standard strip theme. */
273 break;
274
276 /* Meta Strip. */
277 UI_GetThemeColor3fv(is_selected ? TH_NLA_META_SEL : TH_NLA_META, color);
278 break;
280 /* Transition Strip. */
282 break;
283 }
285 /* Sound Strip. */
286 UI_GetThemeColor3fv(is_selected ? TH_NLA_SOUND_SEL : TH_NLA_SOUND, color);
287 break;
288 default: {
289 /* default to unselected theme. */
291 } break;
292 }
293}
294
295/* helper call for drawing influence/time control curves for a given NLA-strip */
296static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uint pos)
297{
298 const float yheight = ymaxc - yminc;
299
300 /* draw with AA'd line */
301 GPU_line_smooth(true);
303
304 /* Fully opaque line on selected strips. */
305 if (strip->flag & NLASTRIP_FLAG_SELECT) {
306 /* TODO: Use theme setting. */
307 immUniformColor3f(1.0f, 1.0f, 1.0f);
308 }
309 else {
310 immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
311 }
312
313 /* influence -------------------------- */
314 if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
315 const FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0);
316
317 /* plot the curve (over the strip's main region) */
318 if (fcu) {
319 immBegin(GPU_PRIM_LINE_STRIP, abs(int(strip->end - strip->start) + 1));
320
321 /* sample at 1 frame intervals, and draw
322 * - min y-val is yminc, max is y-maxc, so clamp in those regions
323 */
324 for (float cfra = strip->start; cfra <= strip->end; cfra += 1.0f) {
325 float y = evaluate_fcurve(fcu, cfra); /* assume this to be in 0-1 range */
326 CLAMP(y, 0.0f, 1.0f);
327 immVertex2f(pos, cfra, ((y * yheight) + yminc));
328 }
329
330 immEnd();
331 }
332 }
333 else {
334 /* use blend in/out values only if both aren't zero */
335 if ((IS_EQF(strip->blendin, 0.0f) && IS_EQF(strip->blendout, 0.0f)) == 0) {
337
338 /* start of strip - if no blendin, start straight at 1,
339 * otherwise from 0 to 1 over blendin frames */
340 if (IS_EQF(strip->blendin, 0.0f) == 0) {
341 immVertex2f(pos, strip->start, yminc);
342 immVertex2f(pos, strip->start + strip->blendin, ymaxc);
343 }
344 else {
345 immVertex2f(pos, strip->start, ymaxc);
346 }
347
348 /* end of strip */
349 if (IS_EQF(strip->blendout, 0.0f) == 0) {
350 immVertex2f(pos, strip->end - strip->blendout, ymaxc);
351 immVertex2f(pos, strip->end, yminc);
352 }
353 else {
354 immVertex2f(pos, strip->end, ymaxc);
355 }
356
357 immEnd();
358 }
359 }
360
361 /* turn off AA'd lines */
362 GPU_line_smooth(false);
364}
365
366/* helper call to setup dashed-lines for strip outlines */
367static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
368{
369 /* Note that we use dashed shader here, and make it draw solid lines if not muted... */
370 uint shdr_pos = GPU_vertformat_attr_add(
373
374 float viewport_size[4];
375 GPU_viewport_size_get_f(viewport_size);
376 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
377
378 immUniform1i("colors_len", 0); /* Simple dashes. */
379 immUniformColor3fv(color);
380
381 /* line style: dotted for muted */
382 if (muted) {
383 /* dotted - and slightly thicker for readability of the dashes */
384 immUniform1f("dash_width", 5.0f);
385 immUniform1f("udash_factor", 0.4f);
386 GPU_line_width(1.5f);
387 }
388 else {
389 /* solid line */
390 immUniform1f("udash_factor", 2.0f);
391 GPU_line_width(1.0f);
392 }
393
394 return shdr_pos;
395}
396
401static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
402{
404 BLI_assert(adt);
405 if (!adt) {
406 return true;
407 }
408
409 if ((nlt->flag & NLATRACK_DISABLED) == 0) {
410 return true;
411 }
412
414 return adt->actstrip == strip;
415}
416
417/* main call for drawing a single NLA-strip */
418static void nla_draw_strip(SpaceNla *snla,
419 AnimData *adt,
420 NlaTrack *nlt,
421 NlaStrip *strip,
422 View2D *v2d,
423 float yminc,
424 float ymaxc)
425{
426 /* If there is no 'adt', this strip came from nowhere. */
427 BLI_assert(adt);
428 if (!adt) {
429 return;
430 }
431
432 const bool adt_has_solo_track = (adt->flag & ADT_NLA_SOLO_TRACK);
433 const bool is_track_solo = (nlt->flag & NLATRACK_SOLO);
434 const bool is_other_track_soloed = adt_has_solo_track && !is_track_solo;
435
436 const bool muted = ((nlt->flag & NLATRACK_MUTED) || (strip->flag & NLASTRIP_FLAG_MUTED));
437 float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
438 uint shdr_pos;
439
440 /* get color of strip */
441 nla_strip_get_color_inside(adt, strip, color);
442
445
446 /* draw extrapolation info first (as backdrop)
447 * - but this should only be drawn if track has some contribution
448 */
449 if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && !is_other_track_soloed) {
450 /* enable transparency... */
452
453 switch (strip->extendmode) {
454 /* since this does both sides,
455 * only do the 'before' side, and leave the rest to the next case */
457 /* only need to draw here if there's no strip before since
458 * it only applies in such a situation
459 */
460 if (strip->prev == nullptr) {
461 /* set the drawing color to the color of the strip, but with very faint alpha */
462 immUniformColor3fvAlpha(color, 0.15f);
463
464 /* draw the rect to the edge of the screen */
465 immRectf(shdr_pos, v2d->cur.xmin, yminc, strip->start, ymaxc);
466 }
468
469 /* this only draws after the strip */
471 /* only need to try and draw if the next strip doesn't occur immediately after */
472 if ((strip->next == nullptr) || (IS_EQF(strip->next->start, strip->end) == 0)) {
473 /* set the drawing color to the color of the strip, but this time less faint */
474 immUniformColor3fvAlpha(color, 0.3f);
475
476 /* draw the rect to the next strip or the edge of the screen */
477 float x2 = strip->next ? strip->next->start : v2d->cur.xmax;
478 immRectf(shdr_pos, strip->end, yminc, x2, ymaxc);
479 }
480 break;
481 }
482
484 }
485
486 /* draw 'inside' of strip itself */
487 const bool is_invalid_location = (strip->flag & NLASTRIP_FLAG_INVALID_LOCATION);
488 if (!is_other_track_soloed && is_nlastrip_enabled(adt, nlt, strip) && !is_invalid_location) {
490
491 /* strip is in normal track */
492 UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */
493 rctf rect;
494 rect.xmin = strip->start;
495 rect.xmax = strip->end;
496 rect.ymin = yminc;
497 rect.ymax = ymaxc;
498 UI_draw_roundbox_4fv(&rect, true, 0.0f, color);
499
500 /* restore current vertex format & program (roundbox trashes it) */
503 }
504 else {
505 /* strip is in disabled track - make less visible */
506 immUniformColor3fvAlpha(color, 0.1f);
507
509 immRectf(shdr_pos, strip->start, yminc, strip->end, ymaxc);
511 }
512
513 /* draw strip's control 'curves'
514 * - only if user hasn't hidden them...
515 */
516 if ((snla->flag & SNLA_NOSTRIPCURVES) == 0) {
517 nla_draw_strip_curves(strip, yminc, ymaxc, shdr_pos);
518 }
519
521
522 /* draw markings indicating locations of local markers
523 * (useful for lining up different actions) */
524 if ((snla->flag & SNLA_NOLOCALMARKERS) == 0) {
525 nla_strip_draw_markers(strip, yminc, ymaxc);
526 }
527
528 /* draw strip outline
529 * - color used here is to indicate active vs non-active
530 */
531 if (is_invalid_location) {
532 color[0] = 1.0f;
533 color[1] = color[2] = 0.15f;
534 }
535 else if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
536 /* strip should appear 'sunken', so draw a light border around it */
537 color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */
538 }
539 else {
540 /* strip should appear to stand out, so draw a dark border around it */
541 color[0] = color[1] = color[2] = 0.0f; /* FIXME: or 1.0f ?? */
542 }
543
544 /* draw outline
545 * - dashed-line shader is loaded after this block
546 */
547 if (muted) {
548 /* muted - draw dotted, squarish outline (for simplicity) */
549 shdr_pos = nla_draw_use_dashed_outlines(color, muted);
550 imm_draw_box_wire_2d(shdr_pos, strip->start, yminc, strip->end, ymaxc);
551 }
552 else {
553 /* non-muted - draw solid, rounded outline */
554 rctf rect;
555 rect.xmin = strip->start;
556 rect.xmax = strip->end;
557 rect.ymin = yminc;
558 rect.ymax = ymaxc;
559 UI_draw_roundbox_4fv(&rect, false, 0.0f, color);
560
561 /* restore current vertex format & program (roundbox trashes it) */
562 shdr_pos = nla_draw_use_dashed_outlines(color, muted);
563 }
564
565 /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */
566 if ((strip->type == NLASTRIP_TYPE_CLIP) && strip->repeat > 1.0f) {
567 float repeatLen = (strip->actend - strip->actstart) * strip->scale;
568
569 /* only draw lines for whole-numbered repeats, starting from the first full-repeat
570 * up to the last full repeat (but not if it lies on the end of the strip)
571 */
573 for (int i = 1; i < strip->repeat; i++) {
574 float repeatPos = strip->start + (repeatLen * i);
575
576 /* don't draw if line would end up on or after the end of the strip */
577 if (repeatPos < strip->end) {
578 immVertex2f(shdr_pos, repeatPos, yminc + 4);
579 immVertex2f(shdr_pos, repeatPos, ymaxc - 4);
580 }
581 }
582 immEnd();
583 }
584 /* or if meta-strip, draw lines delimiting extents of sub-strips
585 * (in same color as outline, if more than 1 exists) */
586 else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) {
587 const float y = (ymaxc - yminc) * 0.5f + yminc;
588
589 /* up to 2 lines per strip */
591
592 /* only draw first-level of child-strips, but don't draw any lines on the endpoints */
593 LISTBASE_FOREACH (NlaStrip *, cs, &strip->strips) {
594 /* draw start-line if not same as end of previous (and only if not the first strip)
595 * - on upper half of strip
596 */
597 if ((cs->prev) && IS_EQF(cs->prev->end, cs->start) == 0) {
598 immVertex2f(shdr_pos, cs->start, y);
599 immVertex2f(shdr_pos, cs->start, ymaxc);
600 }
601
602 /* draw end-line if not the last strip
603 * - on lower half of strip
604 */
605 if (cs->next) {
606 immVertex2f(shdr_pos, cs->end, yminc);
607 immVertex2f(shdr_pos, cs->end, y);
608 }
609 }
610
611 immEnd();
612 }
613
615}
616
619 NlaTrack *nlt,
620 NlaStrip *strip,
621 View2D *v2d,
622 float xminc,
623 float xmaxc,
624 float yminc,
625 float ymaxc)
626{
627 const bool solo = !((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
628 (nlt->flag & NLATRACK_SOLO) == 0);
629
630 char str[256];
631 size_t str_len;
632 uchar col[4];
633
634 /* just print the name and the range */
635 if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
636 str_len = STRNCPY_RLEN(str, DATA_("Temp-Meta"));
637 }
638 else {
639 str_len = STRNCPY_RLEN(str, strip->name);
640 }
641
642 /* set text color - if colors (see above) are light, draw black text, otherwise draw white */
644 col[0] = col[1] = col[2] = 0;
645 }
646 else {
647 col[0] = col[1] = col[2] = 255;
648 }
649 /* Default strip to 100% opacity. */
650 col[3] = 255;
651
652 /* Reduce text opacity if a track is soloed,
653 * and if target track isn't the soloed track. */
654 if (!solo) {
655 col[3] = 128;
656 }
657
658 /* set bounding-box for text
659 * - padding of 2 'units' on either side
660 */
661 /* TODO: make this centered? */
662 rctf rect;
663 rect.xmin = xminc;
664 rect.ymin = yminc;
665 rect.xmax = xmaxc;
666 rect.ymax = ymaxc;
667
668 /* add this string to the cache of texts to draw */
669 UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
670}
671
677 NlaTrack * /*nlt*/, NlaStrip *strip, View2D *v2d, float /*yminc*/, float ymaxc)
678{
679 const float ytol = 1.0f; /* small offset to vertical positioning of text, for legibility */
680 const uchar col[4] = {220, 220, 220, 255}; /* light gray */
681 char numstr[32];
682 size_t numstr_len;
683
684 /* Always draw times above the strip, whereas sequencer drew below + above.
685 * However, we should be fine having everything on top, since these tend to be
686 * quite spaced out.
687 * NOTE: 1 decimal point is a compromise between lack of precision (ints only, as per sequencer)
688 * while also preserving some accuracy, since we do use floats. */
689
690 /* start frame */
691 numstr_len = SNPRINTF_RLEN(numstr, "%.1f", strip->start);
692 UI_view2d_text_cache_add(v2d, strip->start - 1.0f, ymaxc + ytol, numstr, numstr_len, col);
693
694 /* end frame */
695 numstr_len = SNPRINTF_RLEN(numstr, "%.1f", strip->end);
696 UI_view2d_text_cache_add(v2d, strip->end, ymaxc + ytol, numstr, numstr_len, col);
697}
698
699/* ---------------------- */
700
707{
708 if (BLI_listbase_is_empty(&nlt->strips)) {
709 ListBase empty = {nullptr, nullptr};
710 return empty;
711 }
712
713 NlaStrip *first = nullptr;
714 NlaStrip *last = nullptr;
715
716 /* Find the first strip that is within the bounds of the view. */
717 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
718 if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
719 first = last = strip;
720 break;
721 }
722 }
723
724 const bool has_strips_within_bounds = first != nullptr;
725
726 if (has_strips_within_bounds) {
727 /* Find the last visible strip. */
728 for (NlaStrip *strip = first->next; strip; strip = strip->next) {
729 if (!BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
730 break;
731 }
732 last = strip;
733 }
734 /* Check if the first strip is adjacent to a strip outside the view to the left
735 * that has an extendmode region that should be drawn.
736 * If so, adjust the first strip to include drawing that strip as well.
737 */
738 NlaStrip *prev = first->prev;
739 if (prev && prev->extendmode != NLASTRIP_EXTEND_NOTHING) {
740 first = prev;
741 }
742 }
743 else {
744 /* No immediately visible strips.
745 * Figure out where our view is relative to the strips, then determine
746 * if the view is adjacent to a strip that should have its extendmode
747 * rendered.
748 */
749 NlaStrip *first_strip = static_cast<NlaStrip *>(nlt->strips.first);
750 NlaStrip *last_strip = static_cast<NlaStrip *>(nlt->strips.last);
751 if (first_strip && v2d->cur.xmax < first_strip->start &&
752 first_strip->extendmode == NLASTRIP_EXTEND_HOLD)
753 {
754 /* The view is to the left of all strips and the first strip has an
755 * extendmode that should be drawn.
756 */
757 first = last = first_strip;
758 }
759 else if (last_strip && v2d->cur.xmin > last_strip->end &&
760 last_strip->extendmode != NLASTRIP_EXTEND_NOTHING)
761 {
762 /* The view is to the right of all strips and the last strip has an
763 * extendmode that should be drawn.
764 */
765 first = last = last_strip;
766 }
767 else {
768 /* The view is in the middle of two strips. */
769 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
770 /* Find the strip to the left by finding the strip to the right and getting its prev. */
771 if (v2d->cur.xmax < strip->start) {
772 /* If the strip to the left has an extendmode, set that as the only visible strip. */
773 if (strip->prev && strip->prev->extendmode != NLASTRIP_EXTEND_NOTHING) {
774 first = last = strip->prev;
775 }
776 break;
777 }
778 }
779 }
780 }
781
782 ListBase visible_strips = {first, last};
783 return visible_strips;
784}
785
787{
788 View2D *v2d = &region->v2d;
789 const float pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
790 const float text_margin_x = (8 * UI_SCALE_FAC) * pixelx;
791
792 /* build list of tracks to draw */
793 ListBase anim_data = {nullptr, nullptr};
796 size_t items = ANIM_animdata_filter(
797 ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
798
799 /* Update max-extent of tracks here (taking into account scrollers):
800 * - this is done to allow the track list to be scrollable, but must be done here
801 * to avoid regenerating the list again and/or also because tracks list is drawn first
802 * - offset of NLATRACK_HEIGHT*2 is added to the height of the tracks, as first is for
803 * start of list offset, and the second is as a correction for the scrollers.
804 */
805 int height = NLATRACK_TOT_HEIGHT(ac, items);
806 v2d->tot.ymin = -height;
807
808 /* Loop through tracks, and set up drawing depending on their type. */
809 float ymax = NLATRACK_FIRST_TOP(ac);
810
811 for (bAnimListElem *ale = static_cast<bAnimListElem *>(anim_data.first); ale;
812 ale = ale->next, ymax -= NLATRACK_STEP(snla))
813 {
814 float ymin = ymax - NLATRACK_HEIGHT(snla);
815 float ycenter = (ymax + ymin + 2 * NLATRACK_SKIP - 1) / 2.0f;
816
817 /* check if visible */
818 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
819 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
820 {
821 /* data to draw depends on the type of track */
822 switch (ale->type) {
823 case ANIMTYPE_NLATRACK: {
824 AnimData *adt = ale->adt;
825 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
826 ListBase visible_nla_strips = get_visible_nla_strips(nlt, v2d);
827
828 /* Draw each visible strip in the track. */
829 LISTBASE_FOREACH (NlaStrip *, strip, &visible_nla_strips) {
830 const float xminc = strip->start + text_margin_x;
831 const float xmaxc = strip->end - text_margin_x;
832
833 /* draw the visualization of the strip */
834 nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax);
835
836 /* add the text for this strip to the cache */
837 if (xminc < xmaxc) {
838 nla_draw_strip_text(adt, nlt, strip, v2d, xminc, xmaxc, ymin, ymax);
839 }
840
841 /* if transforming strips (only real reason for temp-metas currently),
842 * add to the cache the frame numbers of the strip's extents
843 */
844 if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
845 nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax);
846 }
847 }
848 break;
849 }
850 case ANIMTYPE_NLAACTION: {
851 AnimData *adt = ale->adt;
852
853 /* Draw the manually set intended playback frame range highlight. */
854 if (ale->data) {
855 ANIM_draw_action_framerange(adt, static_cast<bAction *>(ale->data), v2d, ymin, ymax);
856 }
857
861
862 /* just draw a semi-shaded rect spanning the width of the viewable area, based on if
863 * there's data and the action's extrapolation mode. Draw a second darker rect within
864 * which we draw keyframe indicator dots if there's data.
865 */
867
868 /* get colors for drawing */
869 float color[4];
870 nla_action_get_color(adt, static_cast<bAction *>(ale->data), color);
871 immUniformColor4fv(color);
872
873 /* draw slightly shifted up for greater separation from standard tracks,
874 * but also slightly shorter for some more contrast when viewing the strips
875 */
876 switch (adt->act_extendmode) {
879 v2d->cur.xmin,
880 ymin + NLATRACK_SKIP,
881 v2d->cur.xmax,
882 ymax + NLATRACK_SKIP - 1);
883 break;
884 }
886 if (ale->data == nullptr) {
887 /* This can happen if the object itself has no action attached anymore (e.g. after
888 * using "push down"). */
889 break;
890 }
891 const animrig::Action &action = static_cast<bAction *>(ale->data)->wrap();
892 float2 frame_range = action.get_frame_range();
893 BKE_nla_clip_length_ensure_nonzero(&frame_range[0], &frame_range[1]);
894
895 immRectf(
896 pos, frame_range[1], ymin + NLATRACK_SKIP, v2d->cur.xmax, ymax - NLATRACK_SKIP);
897 break;
898 }
900 break;
901 }
902
904
905 /* draw keyframes in the action */
907 adt,
908 static_cast<bAction *>(ale->data),
909 ycenter,
910 ymin + NLATRACK_SKIP,
911 ymax + NLATRACK_SKIP - 1);
912
914 break;
915 }
916 case ANIMTYPE_NONE:
919 case ANIMTYPE_SUMMARY:
920 case ANIMTYPE_SCENE:
921 case ANIMTYPE_OBJECT:
922 case ANIMTYPE_GROUP:
923 case ANIMTYPE_FCURVE:
930 case ANIMTYPE_DSMAT:
931 case ANIMTYPE_DSLAM:
932 case ANIMTYPE_DSCAM:
934 case ANIMTYPE_DSCUR:
935 case ANIMTYPE_DSSKEY:
936 case ANIMTYPE_DSWOR:
937 case ANIMTYPE_DSNTREE:
938 case ANIMTYPE_DSPART:
939 case ANIMTYPE_DSMBALL:
940 case ANIMTYPE_DSARM:
941 case ANIMTYPE_DSMESH:
942 case ANIMTYPE_DSTEX:
943 case ANIMTYPE_DSLAT:
945 case ANIMTYPE_DSSPK:
947 case ANIMTYPE_DSMCLIP:
948 case ANIMTYPE_DSHAIR:
953 case ANIMTYPE_GPLAYER:
959 case ANIMTYPE_PALETTE:
961 break;
962 }
963 }
964 }
965
966 /* Free temporary tracks. */
967 ANIM_animdata_freelist(&anim_data);
968}
969
970/* *********************************************** */
971/* Track List */
972
974 bAnimContext *ac,
975 ARegion *region,
976 const ListBase /*bAnimListElem*/ &anim_data)
977{
978
979 SpaceNla *snla = reinterpret_cast<SpaceNla *>(ac->sl);
980 View2D *v2d = &region->v2d;
981
982 /* need to do a view-sync here, so that the keys area doesn't jump around
983 * (it must copy this) */
984 UI_view2d_sync(nullptr, ac->area, v2d, V2D_LOCK_COPY);
985
986 /* draw tracks */
987 { /* first pass: just the standard GL-drawing for backdrop + text */
988 size_t track_index = 0;
989 float ymax = NLATRACK_FIRST_TOP(ac);
990
991 for (bAnimListElem *ale = static_cast<bAnimListElem *>(anim_data.first); ale;
992 ale = ale->next, ymax -= NLATRACK_STEP(snla), track_index++)
993 {
994 float ymin = ymax - NLATRACK_HEIGHT(snla);
995
996 /* check if visible */
997 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
998 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
999 {
1000 /* draw all tracks using standard channel-drawing API */
1001 ANIM_channel_draw(ac, ale, ymin, ymax, track_index);
1002 }
1003 }
1004 }
1005 { /* second pass: UI widgets */
1006 uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
1007 size_t track_index = 0;
1008 float ymax = NLATRACK_FIRST_TOP(ac);
1009
1010 /* set blending again, as may not be set in previous step */
1012
1013 /* Loop through tracks, and set up drawing depending on their type. */
1014 for (bAnimListElem *ale = static_cast<bAnimListElem *>(anim_data.first); ale;
1015 ale = ale->next, ymax -= NLATRACK_STEP(snla), track_index++)
1016 {
1017 float ymin = ymax - NLATRACK_HEIGHT(snla);
1018
1019 /* check if visible */
1020 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
1021 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1022 {
1023 /* draw all tracks using standard channel-drawing API */
1024 rctf track_rect;
1025 BLI_rctf_init(&track_rect, 0, v2d->cur.xmax, ymin, ymax);
1026 ANIM_channel_draw_widgets(C, ac, ale, block, &track_rect, track_index);
1027 }
1028 }
1029
1030 UI_block_end(C, block);
1031 UI_block_draw(C, block);
1032
1034 }
1035}
1036
1037/* *********************************************** */
Blender kernel action and pose functionality.
FCurve * BKE_fcurve_find(ListBase *list, const char rna_path[], int array_index)
float evaluate_fcurve(const FCurve *fcu, float evaltime)
void BKE_nla_clip_length_ensure_nonzero(const float *actstart, float *r_actend)
@ NLATIME_CONVERT_MAP
Definition BKE_nla.hh:516
bool BKE_nlastrip_within_bounds(NlaStrip *strip, float min, float max)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define ATTR_FALLTHROUGH
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float min_ff(float a, float b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.c:408
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:197
#define STRNCPY_RLEN(dst, src)
Definition BLI_string.h:596
#define SNPRINTF_RLEN(dst, format,...)
Definition BLI_string.h:598
unsigned char uchar
unsigned int uint
#define CLAMP(a, b, c)
#define IN_RANGE(a, b, c)
#define ELEM(...)
#define IS_EQF(a, b)
#define DATA_(msgid)
@ NLASTRIP_FLAG_ACTIVE
@ NLASTRIP_FLAG_USR_INFLUENCE
@ NLASTRIP_FLAG_INVALID_LOCATION
@ NLASTRIP_FLAG_TEMP_META
@ NLASTRIP_FLAG_MUTED
@ NLASTRIP_FLAG_SELECT
@ NLASTRIP_FLAG_TWEAKUSER
@ ADT_NLA_SOLO_TRACK
@ ADT_NLA_EDIT_ON
@ NLASTRIP_EXTEND_HOLD_FORWARD
@ NLASTRIP_EXTEND_NOTHING
@ NLASTRIP_EXTEND_HOLD
@ NLASTRIP_TYPE_SOUND
@ NLASTRIP_TYPE_META
@ NLASTRIP_TYPE_TRANSITION
@ NLASTRIP_TYPE_CLIP
@ NLATRACK_SOLO
@ NLATRACK_MUTED
@ NLATRACK_DISABLED
@ SNLA_NOSTRIPCURVES
@ SNLA_NOLOCALMARKERS
#define UI_SCALE_FAC
@ ANIMTYPE_DSSPK
@ ANIMTYPE_DSTEX
@ ANIMTYPE_SUMMARY
@ ANIMTYPE_DSNTREE
@ ANIMTYPE_NLACURVE
@ ANIMTYPE_SHAPEKEY
@ ANIMTYPE_DSMBALL
@ ANIMTYPE_DSCAM
@ ANIMTYPE_DSPOINTCLOUD
@ ANIMTYPE_FILLDRIVERS
@ ANIMTYPE_NONE
@ ANIMTYPE_DSPART
@ ANIMTYPE_DSLINESTYLE
@ ANIMTYPE_GROUP
@ ANIMTYPE_ACTION_SLOT
@ ANIMTYPE_SPECIALDATA__UNUSED
@ ANIMTYPE_GREASE_PENCIL_DATABLOCK
@ ANIMTYPE_DSCUR
@ ANIMTYPE_SCENE
@ ANIMTYPE_DSARM
@ ANIMTYPE_NLACONTROLS
@ ANIMTYPE_GPLAYER
@ ANIMTYPE_MASKDATABLOCK
@ ANIMTYPE_ANIMDATA
@ ANIMTYPE_MASKLAYER
@ ANIMTYPE_DSGPENCIL
@ ANIMTYPE_DSLAT
@ ANIMTYPE_NLAACTION
@ ANIMTYPE_DSMCLIP
@ ANIMTYPE_DSMAT
@ ANIMTYPE_NUM_TYPES
@ ANIMTYPE_DSCACHEFILE
@ ANIMTYPE_DSVOLUME
@ ANIMTYPE_FCURVE
@ ANIMTYPE_DSLAM
@ ANIMTYPE_PALETTE
@ ANIMTYPE_GPDATABLOCK
@ ANIMTYPE_GREASE_PENCIL_LAYER
@ ANIMTYPE_FILLACT_LAYERED
@ ANIMTYPE_FILLACTD
@ ANIMTYPE_OBJECT
@ ANIMTYPE_DSMESH
@ ANIMTYPE_GREASE_PENCIL_LAYER_GROUP
@ ANIMTYPE_NLATRACK
@ ANIMTYPE_DSWOR
@ ANIMTYPE_DSSKEY
@ ANIMTYPE_DSHAIR
#define NLATRACK_SKIP
#define NLATRACK_FIRST_TOP(ac)
#define NLATRACK_TOT_HEIGHT(ac, item_amount)
#define NLATRACK_STEP(snla)
eAnimCont_Types
eAnimFilter_Flags
@ ANIMFILTER_DATA_VISIBLE
@ ANIMFILTER_LIST_VISIBLE
@ ANIMFILTER_LIST_CHANNELS
@ ANIMFILTER_FCURVESONLY
#define NLATRACK_HEIGHT(snla)
@ KEYFRAME_SHAPE_FRAME
@ KEYFRAME_HANDLE_NONE
@ KEYFRAME_EXTREME_NONE
void immEnd()
void immUnbindProgram()
void immUniform2f(const char *name, float x, float y)
void immUniformColor4f(float r, float g, float b, float a)
void immVertex2f(uint attr_id, float x, float y)
void immUniformThemeColorShade(int color_id, int offset)
void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
void immUniform1i(const char *name, int x)
void immBeginAtMost(GPUPrimType, uint max_vertex_len)
void immUniform1f(const char *name, float x)
GPUVertFormat * immVertexFormat()
void immUniformColor4fv(const float rgba[4])
void immUniformColor3f(float r, float g, float b)
void immBegin(GPUPrimType, uint vertex_len)
void immUniformColor3fvAlpha(const float rgb[3], float a)
void immUniformColor3fv(const float rgb[3])
void imm_draw_box_wire_2d(uint pos, float x1, float y1, float x2, float y2)
void immRectf(uint pos, float x1, float y1, float x2, float y2)
@ GPU_PRIM_LINES
@ GPU_PRIM_POINTS
@ GPU_PRIM_LINE_STRIP
@ GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR
@ GPU_SHADER_KEYFRAME_SHAPE
@ GPU_SHADER_3D_UNIFORM_COLOR
void GPU_program_point_size(bool enable)
Definition gpu_state.cc:175
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(eGPUBlend blend)
Definition gpu_state.cc:42
void GPU_line_width(float width)
Definition gpu_state.cc:161
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:262
@ GPU_FETCH_FLOAT
@ GPU_FETCH_INT_TO_FLOAT_UNIT
@ GPU_FETCH_INT
uint GPU_vertformat_attr_add(GPUVertFormat *, const char *name, GPUVertCompType, uint comp_len, GPUVertFetchMode)
@ GPU_COMP_F32
@ GPU_COMP_U32
@ GPU_COMP_U8
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
@ UI_EMBOSS
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, eUIEmbossType emboss)
void UI_draw_roundbox_corner_set(int type)
void UI_block_draw(const bContext *C, uiBlock *block)
@ UI_CNR_ALL
void UI_block_end(const bContext *C, uiBlock *block)
void UI_GetThemeColor3fv(int colorid, float col[3])
@ TH_NLA_TRANSITION_SEL
@ TH_NLA_META
@ TH_NLA_META_SEL
@ TH_ANIM_ACTIVE
@ TH_NLA_TWEAK
@ TH_STRIP
@ TH_ANIM_INACTIVE
@ TH_NLA_TWEAK_DUPLI
@ TH_NLA_SOUND
@ TH_NLA_TRANSITION
@ TH_STRIP_SELECT
@ TH_NLA_SOUND_SEL
void UI_GetThemeColor4fv(int colorid, float col[4])
void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag)
Definition view2d.cc:861
#define V2D_LOCK_COPY
Definition UI_view2d.hh:85
char char char char void UI_view2d_text_cache_add(View2D *v2d, float x, float y, const char *str, size_t str_len, const unsigned char col[4])
Definition view2d.cc:2077
void UI_view2d_text_cache_add_rectf(View2D *v2d, const rctf *rect_view, const char *str, size_t str_len, const unsigned char col[4])
Definition view2d.cc:2107
void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListElem *ale, uiBlock *block, const rctf *rect, size_t channel_index)
void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc, size_t channel_index)
void ANIM_animdata_freelist(ListBase *anim_data)
Definition anim_deps.cc:457
void ANIM_draw_action_framerange(AnimData *adt, bAction *action, View2D *v2d, float ymin, float ymax)
Definition anim_draw.cc:143
size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, const eAnimFilter_Flags filter_mode, void *data, const eAnimCont_Types datatype)
float nlastrip_get_frame(NlaStrip *strip, float cframe, short mode)
float2 get_frame_range() const ATTR_WARN_UNUSED_RESULT
#define floorf(x)
#define str(s)
uint col
void draw_keyframe_shape(const float x, const float y, float size, const bool sel, const eBezTriple_KeyframeType key_type, const eKeyframeShapeDrawOpts mode, const float alpha, const KeyframeShaderBindings *sh_bindings, const short handle_type, const short extreme_type)
bool ED_keylist_all_keys_frame_range(const AnimKeylist *keylist, Range2f *r_frame_range)
AnimKeylist * ED_keylist_create()
void ED_keylist_free(AnimKeylist *keylist)
const ListBase * ED_keylist_listbase(const AnimKeylist *keylist)
bool ED_keylist_is_empty(const AnimKeylist *keylist)
void action_to_keylist(AnimData *adt, bAction *dna_action, AnimKeylist *keylist, const int saction_flag, blender::float2 range)
format
static void nla_draw_strip_text(AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float xminc, float xmaxc, float yminc, float ymaxc)
Definition nla_draw.cc:618
static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
Definition nla_draw.cc:401
static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
Definition nla_draw.cc:216
static void nla_action_draw_keyframes(View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax)
Definition nla_draw.cc:79
static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uint pos)
Definition nla_draw.cc:296
static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float color[3])
Definition nla_draw.cc:244
static void nla_draw_strip_frames_text(NlaTrack *, NlaStrip *strip, View2D *v2d, float, float ymaxc)
Definition nla_draw.cc:676
static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc)
Definition nla_draw.cc:418
static void nla_actionclip_draw_markers(NlaStrip *strip, float yminc, float ymaxc, int shade, const bool dashed)
Definition nla_draw.cc:171
static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
Definition nla_draw.cc:367
static ListBase get_visible_nla_strips(NlaTrack *nlt, View2D *v2d)
Definition nla_draw.cc:706
void draw_nla_track_list(const bContext *C, bAnimContext *ac, ARegion *region, const ListBase &anim_data)
Definition nla_draw.cc:973
void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
Definition nla_draw.cc:54
void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
Definition nla_draw.cc:786
float wrap(float value, float max, float min)
Definition node_math.h:71
NlaStrip * actstrip
short act_extendmode
void * last
void * first
struct NlaStrip * next
ListBase fcurves
char name[64]
ListBase strips
struct NlaStrip * prev
short extendmode
bAction * act
ListBase strips
float min
Definition BLI_range.h:16
float max
Definition BLI_range.h:17
ListBase markers
SpaceLink * sl
eAnimCont_Types datatype
ScrArea * area
float xmax
float xmin
float ymax
float ymin
ccl_device_inline int abs(int x)
Definition util/math.h:120