Blender V5.0
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
8
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_screen_types.h"
17#include "DNA_space_types.h"
18
19#include "BLI_bounds_types.hh"
20#include "BLI_listbase.h"
21#include "BLI_string_utf8.h"
22#include "BLI_utildefines.h"
23
24#include "BLT_translation.hh"
25
26#include "BKE_fcurve.hh"
27#include "BKE_nla.hh"
28
29#include "ED_anim_api.hh"
30#include "ED_keyframes_draw.hh"
32
33#include "GPU_immediate.hh"
34#include "GPU_immediate_util.hh"
35#include "GPU_state.hh"
36
37#include "WM_types.hh"
38
39#include "UI_interface.hh"
40#include "UI_resources.hh"
41#include "UI_view2d.hh"
42
43#include "nla_intern.hh" /* own include */
44#include "nla_private.h"
45
46using namespace blender;
47
48/* *********************************************** */
49/* Strips */
50
51/* Action-Line ---------------------- */
52
53void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
54{
55 if (adt && (adt->flag & ADT_NLA_EDIT_ON)) {
56 /* greenish color (same as tweaking strip) */
58 }
59 else {
60 if (act) {
61 /* reddish color - same as dope-sheet summary */
63 }
64 else {
65 /* grayish-red color */
67 }
68 }
69
70 /* when an NLA track is tagged "solo", action doesn't contribute,
71 * so shouldn't be as prominent */
72 if (adt && (adt->flag & ADT_NLA_SOLO_TRACK)) {
73 color[3] *= 0.15f;
74 }
75}
76
77/* draw the keyframes in the specified Action */
79 View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax)
80{
81 if (act == nullptr) {
82 return;
83 }
84
85 /* get a list of the keyframes with NLA-scaling applied */
86 AnimKeylist *keylist = ED_keylist_create();
87 action_to_keylist(adt, act, keylist, 0, {v2d->cur.xmin, v2d->cur.xmax});
88
89 if (ED_keylist_is_empty(keylist)) {
90 ED_keylist_free(keylist);
91 return;
92 }
93
94 /* draw a darkened region behind the strips
95 * - get and reset the background color, this time without the alpha to stand out better
96 * (amplified alpha is used instead, but clamped to avoid 100% opacity)
97 */
98 float color[4];
99 nla_action_get_color(adt, act, color);
100 color[3] = min_ff(0.7f, color[3] * 2.5f);
101
103 uint pos_id = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
104
106
108
109 /* - draw a rect from the first to the last frame (no extra overlaps for now)
110 * that is slightly stumpier than the track background (hardcoded 2-units here)
111 */
112
113 Bounds<float> frame_range;
114 ED_keylist_all_keys_frame_range(keylist, &frame_range);
115 immRectf(pos_id, frame_range.min, ymin + 2, frame_range.max, ymax - 2);
117
118 /* Count keys before drawing. */
119 const ListBase *keys = ED_keylist_listbase(keylist);
120 uint key_len = BLI_listbase_count(keys);
121
122 if (key_len > 0) {
124 KeyframeShaderBindings sh_bindings;
125 sh_bindings.pos_id = GPU_vertformat_attr_add(
126 format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
127 sh_bindings.size_id = GPU_vertformat_attr_add(
128 format, "size", blender::gpu::VertAttrType::SFLOAT_32);
129 sh_bindings.color_id = GPU_vertformat_attr_add(
130 format, "color", blender::gpu::VertAttrType::UNORM_8_8_8_8);
132 format, "outlineColor", blender::gpu::VertAttrType::UNORM_8_8_8_8);
133 sh_bindings.flags_id = GPU_vertformat_attr_add(
134 format, "flags", blender::gpu::VertAttrType::UINT_32);
135
138 immUniform1f("outline_scale", 1.0f);
139 immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
140 immBegin(GPU_PRIM_POINTS, key_len);
141
142 /* - disregard the selection status of keyframes so they draw a certain way
143 * - size is 6.0f which is smaller than the editable keyframes, so that there is a distinction
144 */
145 LISTBASE_FOREACH (const ActKeyColumn *, ak, keys) {
146 draw_keyframe_shape(ak->cfra,
147 y,
148 6.0f,
149 false,
150 ak->key_type,
152 1.0f,
153 &sh_bindings,
156 }
157
158 immEnd();
161 }
162
163 /* free icons */
164 ED_keylist_free(keylist);
165}
166
167/* Strip Markers ------------------------ */
168
169/* Markers inside an action strip */
171 NlaStrip *strip, float yminc, float ymaxc, int shade, const bool dashed)
172{
173 const bAction *act = strip->act;
174
175 if (ELEM(nullptr, act, act->markers.first)) {
176 return;
177 }
178
179 const uint shdr_pos = GPU_vertformat_attr_add(
180 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
181 if (dashed) {
183
184 float viewport_size[4];
185 GPU_viewport_size_get_f(viewport_size);
187 "viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
188
189 immUniform1i("colors_len", 0); /* "simple" mode */
190 immUniform1f("dash_width", 6.0f);
191 immUniform1f("udash_factor", 0.5f);
192 }
193 else {
195 }
197
199 LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
200 if ((marker->frame > strip->actstart) && (marker->frame < strip->actend)) {
201 float frame = nlastrip_get_frame(strip, marker->frame, NLATIME_CONVERT_MAP);
202
203 /* just a simple line for now */
204 /* XXX: draw a triangle instead... */
205 immVertex2f(shdr_pos, frame, yminc + 1);
206 immVertex2f(shdr_pos, frame, ymaxc - 1);
207 }
208 }
209 immEnd();
210
212}
213
214/* Markers inside a NLA-Strip */
215static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
216{
217 GPU_line_width(2.0f);
218
219 if (strip->type == NLASTRIP_TYPE_CLIP) {
220 /* try not to be too conspicuous, while being visible enough when transforming */
221 int shade = (strip->flag & NLASTRIP_FLAG_SELECT) ? -60 : -40;
222
223 /* just draw the markers in this clip */
224 nla_actionclip_draw_markers(strip, yminc, ymaxc, shade, true);
225 }
226 else if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
227 /* just a solid color, so that it is very easy to spot */
228 int shade = 20;
229 /* draw the markers in the first level of strips only (if they are actions) */
230 LISTBASE_FOREACH (NlaStrip *, nls, &strip->strips) {
231 if (nls->type == NLASTRIP_TYPE_CLIP) {
232 nla_actionclip_draw_markers(nls, yminc, ymaxc, shade, false);
233 }
234 }
235 }
236
237 GPU_line_width(1.0f);
238}
239
240/* Strips (Proper) ---------------------- */
241
242/* Get colors for drawing NLA Strips. */
243static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float color[3])
244{
245 const bool is_selected = strip->flag & NLASTRIP_FLAG_SELECT;
246 switch (strip->type) {
248 /* Action Strip. */
249 if (adt && (adt->flag & ADT_NLA_EDIT_ON) && (adt->actstrip == strip)) {
250 /* Active strip tweak - tweak theme is applied only to active edit strip,
251 * not linked-duplicates.
252 */
254 break;
255 }
256
257 if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
258 /* Non-active strip tweak - display warning theme
259 * for non active linked-duplicates.
260 */
262 break;
263 }
264 if (strip->flag & NLASTRIP_FLAG_SELECT) {
265 /* selected. */
267 break;
268 }
269
270 /* unselected - use standard strip theme. */
272 break;
273
275 /* Meta Strip. */
277 break;
279 /* Transition Strip. */
281 break;
282 }
284 /* Sound Strip. */
286 break;
287 default: {
288 /* default to unselected theme. */
290 } break;
291 }
292}
293
294/* helper call for drawing influence/time control curves for a given NLA-strip */
295static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uint pos)
296{
297 const float yheight = ymaxc - yminc;
298
299 /* draw with AA'd line */
300 GPU_line_smooth(true);
302
303 /* Fully opaque line on selected strips. */
304 if (strip->flag & NLASTRIP_FLAG_SELECT) {
305 /* TODO: Use theme setting. */
306 immUniformColor3f(1.0f, 1.0f, 1.0f);
307 }
308 else {
309 immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f);
310 }
311
312 /* influence -------------------------- */
313 if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
314 const FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0);
315
316 /* plot the curve (over the strip's main region) */
317 if (fcu) {
318 immBegin(GPU_PRIM_LINE_STRIP, abs(int(strip->end - strip->start) + 1));
319
320 /* sample at 1 frame intervals, and draw
321 * - min y-val is yminc, max is y-maxc, so clamp in those regions
322 */
323 for (float cfra = strip->start; cfra <= strip->end; cfra += 1.0f) {
324 float y = evaluate_fcurve(fcu, cfra); /* assume this to be in 0-1 range */
325 CLAMP(y, 0.0f, 1.0f);
326 immVertex2f(pos, cfra, ((y * yheight) + yminc));
327 }
328
329 immEnd();
330 }
331 }
332 else {
333 /* use blend in/out values only if both aren't zero */
334 if ((IS_EQF(strip->blendin, 0.0f) && IS_EQF(strip->blendout, 0.0f)) == 0) {
336
337 /* start of strip - if no blendin, start straight at 1,
338 * otherwise from 0 to 1 over blendin frames */
339 if (IS_EQF(strip->blendin, 0.0f) == 0) {
340 immVertex2f(pos, strip->start, yminc);
341 immVertex2f(pos, strip->start + strip->blendin, ymaxc);
342 }
343 else {
344 immVertex2f(pos, strip->start, ymaxc);
345 }
346
347 /* end of strip */
348 if (IS_EQF(strip->blendout, 0.0f) == 0) {
349 immVertex2f(pos, strip->end - strip->blendout, ymaxc);
350 immVertex2f(pos, strip->end, yminc);
351 }
352 else {
353 immVertex2f(pos, strip->end, ymaxc);
354 }
355
356 immEnd();
357 }
358 }
359
360 /* turn off AA'd lines */
361 GPU_line_smooth(false);
363}
364
365/* helper call to setup dashed-lines for strip outlines */
366static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
367{
368 /* Note that we use dashed shader here, and make it draw solid lines if not muted... */
369 uint shdr_pos = GPU_vertformat_attr_add(
370 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
372
373 float viewport_size[4];
374 GPU_viewport_size_get_f(viewport_size);
375 immUniform2f("viewport_size", viewport_size[2] / UI_SCALE_FAC, viewport_size[3] / UI_SCALE_FAC);
376
377 immUniform1i("colors_len", 0); /* Simple dashes. */
379
380 /* line style: dotted for muted */
381 if (muted) {
382 /* dotted - and slightly thicker for readability of the dashes */
383 immUniform1f("dash_width", 5.0f);
384 immUniform1f("udash_factor", 0.4f);
385 GPU_line_width(1.5f);
386 }
387 else {
388 /* solid line */
389 immUniform1f("udash_factor", 2.0f);
390 GPU_line_width(1.0f);
391 }
392
393 return shdr_pos;
394}
395
400static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
401{
403 BLI_assert(adt);
404 if (!adt) {
405 return true;
406 }
407
408 if ((nlt->flag & NLATRACK_DISABLED) == 0) {
409 return true;
410 }
411
413 return adt->actstrip == strip;
414}
415
416/* main call for drawing a single NLA-strip */
417static void nla_draw_strip(SpaceNla *snla,
418 AnimData *adt,
419 NlaTrack *nlt,
420 NlaStrip *strip,
421 View2D *v2d,
422 float yminc,
423 float ymaxc)
424{
425 /* If there is no 'adt', this strip came from nowhere. */
426 BLI_assert(adt);
427 if (!adt) {
428 return;
429 }
430
431 const bool adt_has_solo_track = (adt->flag & ADT_NLA_SOLO_TRACK);
432 const bool is_track_solo = (nlt->flag & NLATRACK_SOLO);
433 const bool is_other_track_soloed = adt_has_solo_track && !is_track_solo;
434
435 const bool muted = ((nlt->flag & NLATRACK_MUTED) || (strip->flag & NLASTRIP_FLAG_MUTED));
436 float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
437 uint shdr_pos;
438
439 /* get color of strip */
441
442 shdr_pos = GPU_vertformat_attr_add(
443 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
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 */
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 */
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) */
501 shdr_pos = GPU_vertformat_attr_add(
502 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
504 }
505 else {
506 /* strip is in disabled track - make less visible */
508
510 immRectf(shdr_pos, strip->start, yminc, strip->end, ymaxc);
512 }
513
514 /* draw strip's control 'curves'
515 * - only if user hasn't hidden them...
516 */
517 if ((snla->flag & SNLA_NOSTRIPCURVES) == 0) {
518 nla_draw_strip_curves(strip, yminc, ymaxc, shdr_pos);
519 }
520
522
523 /* draw markings indicating locations of local markers
524 * (useful for lining up different actions) */
525 if ((snla->flag & SNLA_NOLOCALMARKERS) == 0) {
526 nla_strip_draw_markers(strip, yminc, ymaxc);
527 }
528
529 /* draw strip outline
530 * - color used here is to indicate active vs non-active
531 */
532 if (is_invalid_location) {
533 color[0] = 1.0f;
534 color[1] = color[2] = 0.15f;
535 }
536 else if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
537 /* strip should appear 'sunken', so draw a light border around it */
538 color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */
539 }
540 else {
541 /* strip should appear to stand out, so draw a dark border around it */
542 color[0] = color[1] = color[2] = 0.0f; /* FIXME: or 1.0f ?? */
543 }
544
545 /* draw outline
546 * - dashed-line shader is loaded after this block
547 */
548 if (muted) {
549 /* muted - draw dotted, squarish outline (for simplicity) */
550 shdr_pos = nla_draw_use_dashed_outlines(color, muted);
551 imm_draw_box_wire_2d(shdr_pos, strip->start, yminc, strip->end, ymaxc);
552 }
553 else {
554 /* non-muted - draw solid, rounded outline */
555 rctf rect;
556 rect.xmin = strip->start;
557 rect.xmax = strip->end;
558 rect.ymin = yminc;
559 rect.ymax = ymaxc;
560 UI_draw_roundbox_4fv(&rect, false, 0.0f, color);
561
562 /* restore current vertex format & program (roundbox trashes it) */
563 shdr_pos = nla_draw_use_dashed_outlines(color, muted);
564 }
565
566 /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */
567 if ((strip->type == NLASTRIP_TYPE_CLIP) && strip->repeat > 1.0f) {
568 float repeatLen = (strip->actend - strip->actstart) * strip->scale;
569
570 /* only draw lines for whole-numbered repeats, starting from the first full-repeat
571 * up to the last full repeat (but not if it lies on the end of the strip)
572 */
574 for (int i = 1; i < strip->repeat; i++) {
575 float repeatPos = strip->start + (repeatLen * i);
576
577 /* don't draw if line would end up on or after the end of the strip */
578 if (repeatPos < strip->end) {
579 immVertex2f(shdr_pos, repeatPos, yminc + 4);
580 immVertex2f(shdr_pos, repeatPos, ymaxc - 4);
581 }
582 }
583 immEnd();
584 }
585 /* or if meta-strip, draw lines delimiting extents of sub-strips
586 * (in same color as outline, if more than 1 exists) */
587 else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) {
588 const float y = (ymaxc - yminc) * 0.5f + yminc;
589
590 /* up to 2 lines per strip */
592
593 /* only draw first-level of child-strips, but don't draw any lines on the endpoints */
594 LISTBASE_FOREACH (NlaStrip *, cs, &strip->strips) {
595 /* draw start-line if not same as end of previous (and only if not the first strip)
596 * - on upper half of strip
597 */
598 if ((cs->prev) && IS_EQF(cs->prev->end, cs->start) == 0) {
599 immVertex2f(shdr_pos, cs->start, y);
600 immVertex2f(shdr_pos, cs->start, ymaxc);
601 }
602
603 /* draw end-line if not the last strip
604 * - on lower half of strip
605 */
606 if (cs->next) {
607 immVertex2f(shdr_pos, cs->end, yminc);
608 immVertex2f(shdr_pos, cs->end, y);
609 }
610 }
611
612 immEnd();
613 }
614
616}
617
620 NlaTrack *nlt,
621 NlaStrip *strip,
622 View2D *v2d,
623 float xminc,
624 float xmaxc,
625 float yminc,
626 float ymaxc)
627{
628 const bool solo = !((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
629 (nlt->flag & NLATRACK_SOLO) == 0);
630
631 char str[256];
632 size_t str_len;
633 uchar col[4];
634
635 /* just print the name and the range */
636 if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
637 str_len = STRNCPY_UTF8_RLEN(str, DATA_("Temp-Meta"));
638 }
639 else {
640 str_len = STRNCPY_UTF8_RLEN(str, strip->name);
641 }
642
643 /* set text color - if colors (see above) are light, draw black text, otherwise draw white */
645 col[0] = col[1] = col[2] = 0;
646 }
647 else {
648 col[0] = col[1] = col[2] = 255;
649 }
650 /* Default strip to 100% opacity. */
651 col[3] = 255;
652
653 /* Reduce text opacity if a track is soloed,
654 * and if target track isn't the soloed track. */
655 if (!solo) {
656 col[3] = 128;
657 }
658
659 /* set bounding-box for text
660 * - padding of 2 'units' on either side
661 */
662 /* TODO: make this centered? */
663 rctf rect;
664 rect.xmin = xminc;
665 rect.ymin = yminc;
666 rect.xmax = xmaxc;
667 rect.ymax = ymaxc;
668
669 /* add this string to the cache of texts to draw */
670 UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
671}
672
678 NlaTrack * /*nlt*/, NlaStrip *strip, View2D *v2d, float /*yminc*/, float ymaxc)
679{
680 const float ytol = 1.0f; /* small offset to vertical positioning of text, for legibility */
681 const uchar col[4] = {220, 220, 220, 255}; /* light gray */
682 char numstr[32];
683 size_t numstr_len;
684
685 /* Always draw times above the strip, whereas sequencer drew below + above.
686 * However, we should be fine having everything on top, since these tend to be
687 * quite spaced out.
688 * NOTE: 1 decimal point is a compromise between lack of precision (ints only, as per sequencer)
689 * while also preserving some accuracy, since we do use floats. */
690
691 /* start frame */
692 numstr_len = SNPRINTF_UTF8_RLEN(numstr, "%.1f", strip->start);
693 UI_view2d_text_cache_add(v2d, strip->start - 1.0f, ymaxc + ytol, numstr, numstr_len, col);
694
695 /* end frame */
696 numstr_len = SNPRINTF_UTF8_RLEN(numstr, "%.1f", strip->end);
697 UI_view2d_text_cache_add(v2d, strip->end, ymaxc + ytol, numstr, numstr_len, col);
698}
699
700/* ---------------------- */
701
708{
709 if (BLI_listbase_is_empty(&nlt->strips)) {
710 ListBase empty = {nullptr, nullptr};
711 return empty;
712 }
713
714 NlaStrip *first = nullptr;
715 NlaStrip *last = nullptr;
716
717 /* Find the first strip that is within the bounds of the view. */
718 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
719 if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
720 first = last = strip;
721 break;
722 }
723 }
724
725 const bool has_strips_within_bounds = first != nullptr;
726
727 if (has_strips_within_bounds) {
728 /* Find the last visible strip. */
729 for (NlaStrip *strip = first->next; strip; strip = strip->next) {
730 if (!BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
731 break;
732 }
733 last = strip;
734 }
735 /* Check if the first strip is adjacent to a strip outside the view to the left
736 * that has an extendmode region that should be drawn.
737 * If so, adjust the first strip to include drawing that strip as well.
738 */
739 NlaStrip *prev = first->prev;
740 if (prev && prev->extendmode != NLASTRIP_EXTEND_NOTHING) {
741 first = prev;
742 }
743 }
744 else {
745 /* No immediately visible strips.
746 * Figure out where our view is relative to the strips, then determine
747 * if the view is adjacent to a strip that should have its extendmode
748 * rendered.
749 */
750 NlaStrip *first_strip = static_cast<NlaStrip *>(nlt->strips.first);
751 NlaStrip *last_strip = static_cast<NlaStrip *>(nlt->strips.last);
752 if (first_strip && v2d->cur.xmax < first_strip->start &&
753 first_strip->extendmode == NLASTRIP_EXTEND_HOLD)
754 {
755 /* The view is to the left of all strips and the first strip has an
756 * extendmode that should be drawn.
757 */
758 first = last = first_strip;
759 }
760 else if (last_strip && v2d->cur.xmin > last_strip->end &&
761 last_strip->extendmode != NLASTRIP_EXTEND_NOTHING)
762 {
763 /* The view is to the right of all strips and the last strip has an
764 * extendmode that should be drawn.
765 */
766 first = last = last_strip;
767 }
768 else {
769 /* The view is in the middle of two strips. */
770 LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
771 /* Find the strip to the left by finding the strip to the right and getting its prev. */
772 if (v2d->cur.xmax < strip->start) {
773 /* If the strip to the left has an extendmode, set that as the only visible strip. */
774 if (strip->prev && strip->prev->extendmode != NLASTRIP_EXTEND_NOTHING) {
775 first = last = strip->prev;
776 }
777 break;
778 }
779 }
780 }
781 }
782
783 ListBase visible_strips = {first, last};
784 return visible_strips;
785}
786
788{
789 View2D *v2d = &region->v2d;
790 const float pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
791 const float text_margin_x = (8 * UI_SCALE_FAC) * pixelx;
792
793 /* build list of tracks to draw */
794 ListBase anim_data = {nullptr, nullptr};
797 size_t items = ANIM_animdata_filter(
798 ac, &anim_data, filter, ac->data, eAnimCont_Types(ac->datatype));
799
800 /* Update max-extent of tracks here (taking into account scrollers):
801 * - this is done to allow the track list to be scrollable, but must be done here
802 * to avoid regenerating the list again and/or also because tracks list is drawn first
803 * - offset of NLATRACK_HEIGHT*2 is added to the height of the tracks, as first is for
804 * start of list offset, and the second is as a correction for the scrollers.
805 */
806 int height = NLATRACK_TOT_HEIGHT(ac, items);
807 v2d->tot.ymin = -height;
808
809 /* Loop through tracks, and set up drawing depending on their type. */
810 float ymax = NLATRACK_FIRST_TOP(ac);
811
812 for (bAnimListElem *ale = static_cast<bAnimListElem *>(anim_data.first); ale;
813 ale = ale->next, ymax -= NLATRACK_STEP(snla))
814 {
815 float ymin = ymax - NLATRACK_HEIGHT(snla);
816 float ycenter = (ymax + ymin + 2 * NLATRACK_SKIP - 1) / 2.0f;
817
818 /* check if visible */
819 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
820 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
821 {
822 /* data to draw depends on the type of track */
823 switch (ale->type) {
824 case ANIMTYPE_NLATRACK: {
825 AnimData *adt = ale->adt;
826 NlaTrack *nlt = static_cast<NlaTrack *>(ale->data);
827 ListBase visible_nla_strips = get_visible_nla_strips(nlt, v2d);
828
829 /* Draw each visible strip in the track. */
830 LISTBASE_FOREACH (NlaStrip *, strip, &visible_nla_strips) {
831 const float xminc = strip->start + text_margin_x;
832 const float xmaxc = strip->end - text_margin_x;
833
834 /* draw the visualization of the strip */
835 nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax);
836
837 /* add the text for this strip to the cache */
838 if (xminc < xmaxc) {
839 nla_draw_strip_text(adt, nlt, strip, v2d, xminc, xmaxc, ymin, ymax);
840 }
841
842 /* if transforming strips (only real reason for temp-metas currently),
843 * add to the cache the frame numbers of the strip's extents
844 */
845 if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
846 nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax);
847 }
848 }
849 break;
850 }
851 case ANIMTYPE_NLAACTION: {
852 AnimData *adt = ale->adt;
853
854 /* Draw the manually set intended playback frame range highlight. */
855 if (ale->data) {
856 ANIM_draw_action_framerange(adt, static_cast<bAction *>(ale->data), v2d, ymin, ymax);
857 }
858
860 immVertexFormat(), "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
862
863 /* just draw a semi-shaded rect spanning the width of the viewable area, based on if
864 * there's data and the action's extrapolation mode. Draw a second darker rect within
865 * which we draw keyframe indicator dots if there's data.
866 */
868
869 /* get colors for drawing */
870 float color[4];
871 nla_action_get_color(adt, static_cast<bAction *>(ale->data), color);
873
874 /* draw slightly shifted up for greater separation from standard tracks,
875 * but also slightly shorter for some more contrast when viewing the strips
876 */
877 switch (adt->act_extendmode) {
880 v2d->cur.xmin,
881 ymin + NLATRACK_SKIP,
882 v2d->cur.xmax,
883 ymax + NLATRACK_SKIP - 1);
884 break;
885 }
887 if (ale->data == nullptr) {
888 /* This can happen if the object itself has no action attached anymore (e.g. after
889 * using "push down"). */
890 break;
891 }
892 const animrig::Action &action = static_cast<bAction *>(ale->data)->wrap();
893 float2 frame_range = action.get_frame_range();
894 BKE_nla_clip_length_ensure_nonzero(&frame_range[0], &frame_range[1]);
895
896 immRectf(
897 pos, frame_range[1], ymin + NLATRACK_SKIP, v2d->cur.xmax, ymax - NLATRACK_SKIP);
898 break;
899 }
901 break;
902 }
903
905
906 /* draw keyframes in the action */
908 adt,
909 static_cast<bAction *>(ale->data),
910 ycenter,
911 ymin + NLATRACK_SKIP,
912 ymax + NLATRACK_SKIP - 1);
913
915 break;
916 }
917 case ANIMTYPE_NONE:
920 case ANIMTYPE_SUMMARY:
921 case ANIMTYPE_SCENE:
922 case ANIMTYPE_OBJECT:
923 case ANIMTYPE_GROUP:
924 case ANIMTYPE_FCURVE:
931 case ANIMTYPE_DSMAT:
932 case ANIMTYPE_DSLAM:
933 case ANIMTYPE_DSCAM:
935 case ANIMTYPE_DSCUR:
936 case ANIMTYPE_DSSKEY:
937 case ANIMTYPE_DSWOR:
938 case ANIMTYPE_DSNTREE:
939 case ANIMTYPE_DSPART:
940 case ANIMTYPE_DSMBALL:
941 case ANIMTYPE_DSARM:
942 case ANIMTYPE_DSMESH:
943 case ANIMTYPE_DSTEX:
944 case ANIMTYPE_DSLAT:
946 case ANIMTYPE_DSSPK:
948 case ANIMTYPE_DSMCLIP:
949 case ANIMTYPE_DSHAIR:
954 case ANIMTYPE_GPLAYER:
960 case ANIMTYPE_PALETTE:
962 break;
963 }
964 }
965 }
966
967 /* Free temporary tracks. */
968 ANIM_animdata_freelist(&anim_data);
969}
970
971/* *********************************************** */
972/* Track List */
973
975 bAnimContext *ac,
976 ARegion *region,
977 const ListBase /*bAnimListElem*/ &anim_data)
978{
979
980 SpaceNla *snla = reinterpret_cast<SpaceNla *>(ac->sl);
981 View2D *v2d = &region->v2d;
982
983 /* need to do a view-sync here, so that the keys area doesn't jump around
984 * (it must copy this) */
985 UI_view2d_sync(nullptr, ac->area, v2d, V2D_LOCK_COPY);
986
987 /* draw tracks */
988 { /* first pass: just the standard GL-drawing for backdrop + text */
989 size_t track_index = 0;
990 float ymax = NLATRACK_FIRST_TOP(ac);
991
992 for (bAnimListElem *ale = static_cast<bAnimListElem *>(anim_data.first); ale;
993 ale = ale->next, ymax -= NLATRACK_STEP(snla), track_index++)
994 {
995 float ymin = ymax - NLATRACK_HEIGHT(snla);
996
997 /* check if visible */
998 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
999 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1000 {
1001 /* draw all tracks using standard channel-drawing API */
1002 ANIM_channel_draw(ac, ale, ymin, ymax, track_index);
1003 }
1004 }
1005 }
1006 { /* second pass: UI widgets */
1007 uiBlock *block = UI_block_begin(C, region, __func__, blender::ui::EmbossType::Emboss);
1008 size_t track_index = 0;
1009 float ymax = NLATRACK_FIRST_TOP(ac);
1010
1011 /* set blending again, as may not be set in previous step */
1013
1014 /* Loop through tracks, and set up drawing depending on their type. */
1015 for (bAnimListElem *ale = static_cast<bAnimListElem *>(anim_data.first); ale;
1016 ale = ale->next, ymax -= NLATRACK_STEP(snla), track_index++)
1017 {
1018 float ymin = ymax - NLATRACK_HEIGHT(snla);
1019
1020 /* check if visible */
1021 if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
1022 IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax))
1023 {
1024 /* draw all tracks using standard channel-drawing API */
1025 rctf track_rect;
1026 BLI_rctf_init(&track_rect, 0, v2d->cur.xmax, ymin, ymax);
1027 ANIM_channel_draw_widgets(C, ac, ale, block, &track_rect, track_index);
1028 }
1029 }
1030
1031 UI_block_end(C, block);
1032 UI_block_draw(C, block);
1033
1035 }
1036}
1037
1038/* *********************************************** */
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:552
bool BKE_nlastrip_within_bounds(NlaStrip *strip, float min, float max)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define ATTR_FALLTHROUGH
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
int BLI_listbase_count(const ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:524
MINLINE float min_ff(float a, float b)
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:198
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.cc:404
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
#define SNPRINTF_UTF8_RLEN(dst, format,...)
#define STRNCPY_UTF8_RLEN(dst, src)
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_DSLIGHTPROBE
@ 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_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 immBindBuiltinProgram(GPUBuiltinShader shader_id)
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 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:180
void GPU_line_width(float width)
Definition gpu_state.cc:166
void GPU_line_smooth(bool enable)
Definition gpu_state.cc:78
@ GPU_BLEND_NONE
Definition GPU_state.hh:85
@ GPU_BLEND_ALPHA
Definition GPU_state.hh:87
void GPU_blend(GPUBlend blend)
Definition gpu_state.cc:42
void GPU_viewport_size_get_f(float coords[4])
Definition gpu_state.cc:273
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
#define C
Definition RandGen.cpp:29
void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float col[4])
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType 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:865
#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:2082
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:2112
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:463
void ANIM_draw_action_framerange(AnimData *adt, bAction *action, View2D *v2d, float ymin, float ymax)
Definition anim_draw.cc:207
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 str(s)
uint pos
uint col
#define filter
#define abs
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, Bounds< float > *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:619
static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
Definition nla_draw.cc:400
static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
Definition nla_draw.cc:215
static void nla_action_draw_keyframes(View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax)
Definition nla_draw.cc:78
static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uint pos)
Definition nla_draw.cc:295
static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float color[3])
Definition nla_draw.cc:243
static void nla_draw_strip_frames_text(NlaTrack *, NlaStrip *strip, View2D *v2d, float, float ymaxc)
Definition nla_draw.cc:677
static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc)
Definition nla_draw.cc:417
static void nla_actionclip_draw_markers(NlaStrip *strip, float yminc, float ymaxc, int shade, const bool dashed)
Definition nla_draw.cc:170
static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
Definition nla_draw.cc:366
static ListBase get_visible_nla_strips(NlaTrack *nlt, View2D *v2d)
Definition nla_draw.cc:707
void draw_nla_track_list(const bContext *C, bAnimContext *ac, ARegion *region, const ListBase &anim_data)
Definition nla_draw.cc:974
void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
Definition nla_draw.cc:53
void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
Definition nla_draw.cc:787
float wrap(float value, float max, float min)
Definition node_math.h:103
#define floorf
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
ListBase markers
SpaceLink * sl
eAnimCont_Types datatype
ScrArea * area
bAnimListElem * next
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230