Fix T89291: Objects with rotation deltas don't rotate in correct axes
[blender.git] / source / blender / editors / space_nla / nla_draw.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2009 Blender Foundation, Joshua Leung
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spnla
22  */
23
24 #include <float.h>
25 #include <math.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "DNA_anim_types.h"
31 #include "DNA_node_types.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_space_types.h"
34 #include "DNA_windowmanager_types.h"
35
36 #include "BLI_blenlib.h"
37 #include "BLI_dlrbTree.h"
38 #include "BLI_utildefines.h"
39
40 #include "BKE_context.h"
41 #include "BKE_fcurve.h"
42 #include "BKE_nla.h"
43 #include "BKE_screen.h"
44
45 #include "ED_anim_api.h"
46 #include "ED_keyframes_draw.h"
47
48 #include "GPU_immediate.h"
49 #include "GPU_immediate_util.h"
50 #include "GPU_state.h"
51
52 #include "WM_types.h"
53
54 #include "UI_interface.h"
55 #include "UI_resources.h"
56 #include "UI_view2d.h"
57
58 #include "nla_intern.h" /* own include */
59 #include "nla_private.h"
60
61 /* *********************************************** */
62 /* Strips */
63
64 /* Action-Line ---------------------- */
65
66 /* get colors for drawing Action-Line
67  * NOTE: color returned includes fine-tuned alpha!
68  */
69 void nla_action_get_color(AnimData *adt, bAction *act, float color[4])
70 {
71   if (adt && (adt->flag & ADT_NLA_EDIT_ON)) {
72     /* greenish color (same as tweaking strip) */
73     UI_GetThemeColor4fv(TH_NLA_TWEAK, color);
74   }
75   else {
76     if (act) {
77       /* reddish color - same as dopesheet summary */
78       UI_GetThemeColor4fv(TH_ANIM_ACTIVE, color);
79     }
80     else {
81       /* grayish-red color */
82       UI_GetThemeColor4fv(TH_ANIM_INACTIVE, color);
83     }
84   }
85
86   /* when an NLA track is tagged "solo", action doesn't contribute,
87    * so shouldn't be as prominent */
88   if (adt && (adt->flag & ADT_NLA_SOLO_TRACK)) {
89     color[3] *= 0.15f;
90   }
91 }
92
93 /* draw the keyframes in the specified Action */
94 static void nla_action_draw_keyframes(
95     View2D *v2d, AnimData *adt, bAction *act, float y, float ymin, float ymax)
96 {
97   /* get a list of the keyframes with NLA-scaling applied */
98   DLRBT_Tree keys;
99   BLI_dlrbTree_init(&keys);
100   action_to_keylist(adt, act, &keys, 0);
101
102   if (ELEM(NULL, act, keys.first)) {
103     return;
104   }
105
106   /* draw a darkened region behind the strips
107    * - get and reset the background color, this time without the alpha to stand out better
108    *   (amplified alpha is used instead)
109    */
110   float color[4];
111   nla_action_get_color(adt, act, color);
112   color[3] *= 2.5f;
113
114   GPUVertFormat *format = immVertexFormat();
115   uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
116
117   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
118
119   immUniformColor4fv(color);
120
121   /* - draw a rect from the first to the last frame (no extra overlaps for now)
122    *   that is slightly stumpier than the track background (hardcoded 2-units here)
123    */
124   float f1 = ((ActKeyColumn *)keys.first)->cfra;
125   float f2 = ((ActKeyColumn *)keys.last)->cfra;
126
127   immRectf(pos_id, f1, ymin + 2, f2, ymax - 2);
128   immUnbindProgram();
129
130   /* count keys before drawing */
131   /* Note: It's safe to cast DLRBT_Tree, as it's designed to degrade down to a ListBase */
132   uint key_len = BLI_listbase_count((ListBase *)&keys);
133
134   if (key_len > 0) {
135     format = immVertexFormat();
136     pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
137     uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
138     uint color_id = GPU_vertformat_attr_add(
139         format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
140     uint outline_color_id = GPU_vertformat_attr_add(
141         format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
142     uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
143
144     GPU_program_point_size(true);
145     immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND);
146     immUniform1f("outline_scale", 1.0f);
147     immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1);
148     immBegin(GPU_PRIM_POINTS, key_len);
149
150     /* - disregard the selection status of keyframes so they draw a certain way
151      * - size is 6.0f which is smaller than the editable keyframes, so that there is a distinction
152      */
153     LISTBASE_FOREACH (ActKeyColumn *, ak, &keys) {
154       draw_keyframe_shape(ak->cfra,
155                           y,
156                           6.0f,
157                           false,
158                           ak->key_type,
159                           KEYFRAME_SHAPE_FRAME,
160                           1.0f,
161                           pos_id,
162                           size_id,
163                           color_id,
164                           outline_color_id,
165                           flags_id,
166                           KEYFRAME_HANDLE_NONE,
167                           KEYFRAME_EXTREME_NONE);
168     }
169
170     immEnd();
171     GPU_program_point_size(false);
172     immUnbindProgram();
173   }
174
175   /* free icons */
176   BLI_dlrbTree_free(&keys);
177 }
178
179 /* Strip Markers ------------------------ */
180
181 /* Markers inside an action strip */
182 static void nla_actionclip_draw_markers(
183     NlaStrip *strip, float yminc, float ymaxc, int shade, const bool dashed)
184 {
185   const bAction *act = strip->act;
186
187   if (ELEM(NULL, act, act->markers.first)) {
188     return;
189   }
190
191   const uint shdr_pos = GPU_vertformat_attr_add(
192       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
193   if (dashed) {
194     immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
195
196     float viewport_size[4];
197     GPU_viewport_size_get_f(viewport_size);
198     immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
199
200     immUniform1i("colors_len", 0); /* "simple" mode */
201     immUniform1f("dash_width", 6.0f);
202     immUniform1f("dash_factor", 0.5f);
203   }
204   else {
205     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
206   }
207   immUniformThemeColorShade(TH_STRIP_SELECT, shade);
208
209   immBeginAtMost(GPU_PRIM_LINES, BLI_listbase_count(&act->markers) * 2);
210   LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) {
211     if ((marker->frame > strip->actstart) && (marker->frame < strip->actend)) {
212       float frame = nlastrip_get_frame(strip, marker->frame, NLATIME_CONVERT_MAP);
213
214       /* just a simple line for now */
215       /* XXX: draw a triangle instead... */
216       immVertex2f(shdr_pos, frame, yminc + 1);
217       immVertex2f(shdr_pos, frame, ymaxc - 1);
218     }
219   }
220   immEnd();
221
222   immUnbindProgram();
223 }
224
225 /* Markers inside a NLA-Strip */
226 static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
227 {
228   GPU_line_width(2.0f);
229
230   if (strip->type == NLASTRIP_TYPE_CLIP) {
231     /* try not to be too conspicuous, while being visible enough when transforming */
232     int shade = (strip->flag & NLASTRIP_FLAG_SELECT) ? -60 : -40;
233
234     /* just draw the markers in this clip */
235     nla_actionclip_draw_markers(strip, yminc, ymaxc, shade, true);
236   }
237   else if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
238     /* just a solid color, so that it is very easy to spot */
239     int shade = 20;
240     /* draw the markers in the first level of strips only (if they are actions) */
241     LISTBASE_FOREACH (NlaStrip *, nls, &strip->strips) {
242       if (nls->type == NLASTRIP_TYPE_CLIP) {
243         nla_actionclip_draw_markers(nls, yminc, ymaxc, shade, false);
244       }
245     }
246   }
247
248   GPU_line_width(1.0f);
249 }
250
251 /* Strips (Proper) ---------------------- */
252
253 /* get colors for drawing NLA-Strips */
254 static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float color[3])
255 {
256   if (strip->type == NLASTRIP_TYPE_TRANSITION) {
257     /* Transition Clip */
258     if (strip->flag & NLASTRIP_FLAG_SELECT) {
259       /* selected - use a bright blue color */
260       UI_GetThemeColor3fv(TH_NLA_TRANSITION_SEL, color);
261     }
262     else {
263       /* normal, unselected strip - use (hardly noticeable) blue tinge */
264       UI_GetThemeColor3fv(TH_NLA_TRANSITION, color);
265     }
266   }
267   else if (strip->type == NLASTRIP_TYPE_META) {
268     /* Meta Clip */
269     /* TODO: should temporary meta-strips get different colors too? */
270     if (strip->flag & NLASTRIP_FLAG_SELECT) {
271       /* selected - use a bold purple color */
272       UI_GetThemeColor3fv(TH_NLA_META_SEL, color);
273     }
274     else {
275       /* normal, unselected strip - use (hardly noticeable) dark purple tinge */
276       UI_GetThemeColor3fv(TH_NLA_META, color);
277     }
278   }
279   else if (strip->type == NLASTRIP_TYPE_SOUND) {
280     /* Sound Clip */
281     if (strip->flag & NLASTRIP_FLAG_SELECT) {
282       /* selected - use a bright teal color */
283       UI_GetThemeColor3fv(TH_NLA_SOUND_SEL, color);
284     }
285     else {
286       /* normal, unselected strip - use (hardly noticeable) teal tinge */
287       UI_GetThemeColor3fv(TH_NLA_SOUND, color);
288     }
289   }
290   else {
291     /* Action Clip (default/normal type of strip) */
292     if (adt && (adt->flag & ADT_NLA_EDIT_ON) && (adt->actstrip == strip)) {
293       /* active strip should be drawn green when it is acting as the tweaking strip.
294        * however, this case should be skipped for when not in EditMode...
295        */
296       UI_GetThemeColor3fv(TH_NLA_TWEAK, color);
297     }
298     else if (strip->flag & NLASTRIP_FLAG_TWEAKUSER) {
299       /* alert user that this strip is also used by the tweaking track (this is set when going into
300        * 'editmode' for that strip), since the edits made here may not be what the user anticipated
301        */
302       UI_GetThemeColor3fv(TH_NLA_TWEAK_DUPLI, color);
303     }
304     else if (strip->flag & NLASTRIP_FLAG_SELECT) {
305       /* selected strip - use theme color for selected */
306       UI_GetThemeColor3fv(TH_STRIP_SELECT, color);
307     }
308     else {
309       /* normal, unselected strip - use standard strip theme color */
310       UI_GetThemeColor3fv(TH_STRIP, color);
311     }
312   }
313 }
314
315 /* helper call for drawing influence/time control curves for a given NLA-strip */
316 static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uint pos)
317 {
318   const float yheight = ymaxc - yminc;
319
320   immUniformColor3f(0.7f, 0.7f, 0.7f);
321
322   /* draw with AA'd line */
323   GPU_line_smooth(true);
324   GPU_blend(GPU_BLEND_ALPHA);
325
326   /* influence -------------------------- */
327   if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) {
328     FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0);
329     float cfra;
330
331     /* plot the curve (over the strip's main region) */
332     if (fcu) {
333       immBegin(GPU_PRIM_LINE_STRIP, abs((int)(strip->end - strip->start) + 1));
334
335       /* sample at 1 frame intervals, and draw
336        * - min y-val is yminc, max is y-maxc, so clamp in those regions
337        */
338       for (cfra = strip->start; cfra <= strip->end; cfra += 1.0f) {
339         float y = evaluate_fcurve(fcu, cfra); /* assume this to be in 0-1 range */
340         CLAMP(y, 0.0f, 1.0f);
341         immVertex2f(pos, cfra, ((y * yheight) + yminc));
342       }
343
344       immEnd();
345     }
346   }
347   else {
348     /* use blend in/out values only if both aren't zero */
349     if ((IS_EQF(strip->blendin, 0.0f) && IS_EQF(strip->blendout, 0.0f)) == 0) {
350       immBeginAtMost(GPU_PRIM_LINE_STRIP, 4);
351
352       /* start of strip - if no blendin, start straight at 1,
353        * otherwise from 0 to 1 over blendin frames */
354       if (IS_EQF(strip->blendin, 0.0f) == 0) {
355         immVertex2f(pos, strip->start, yminc);
356         immVertex2f(pos, strip->start + strip->blendin, ymaxc);
357       }
358       else {
359         immVertex2f(pos, strip->start, ymaxc);
360       }
361
362       /* end of strip */
363       if (IS_EQF(strip->blendout, 0.0f) == 0) {
364         immVertex2f(pos, strip->end - strip->blendout, ymaxc);
365         immVertex2f(pos, strip->end, yminc);
366       }
367       else {
368         immVertex2f(pos, strip->end, ymaxc);
369       }
370
371       immEnd();
372     }
373   }
374
375   /* turn off AA'd lines */
376   GPU_line_smooth(false);
377   GPU_blend(GPU_BLEND_NONE);
378 }
379
380 /* helper call to setup dashed-lines for strip outlines */
381 static uint nla_draw_use_dashed_outlines(const float color[4], bool muted)
382 {
383   /* Note that we use dashed shader here, and make it draw solid lines if not muted... */
384   uint shdr_pos = GPU_vertformat_attr_add(
385       immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
386   immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
387
388   float viewport_size[4];
389   GPU_viewport_size_get_f(viewport_size);
390   immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
391
392   immUniform1i("colors_len", 0); /* Simple dashes. */
393   immUniformColor3fv(color);
394
395   /* line style: dotted for muted */
396   if (muted) {
397     /* dotted - and slightly thicker for readability of the dashes */
398     immUniform1f("dash_width", 5.0f);
399     immUniform1f("dash_factor", 0.4f);
400     GPU_line_width(1.5f);
401   }
402   else {
403     /* solid line */
404     immUniform1f("dash_factor", 2.0f);
405     GPU_line_width(1.0f);
406   }
407
408   return shdr_pos;
409 }
410
411 /**
412  * This check only accounts for the track's disabled flag and whether the strip is being tweaked.
413  * It does not account for muting or soloing.
414  */
415 static bool is_nlastrip_enabled(AnimData *adt, NlaTrack *nlt, NlaStrip *strip)
416 {
417   /** This shouldn't happen. If passed NULL, then just treat strip as enabled. */
418   BLI_assert(adt);
419   if (!adt) {
420     return true;
421   }
422
423   if ((nlt->flag & NLATRACK_DISABLED) == 0) {
424     return true;
425   }
426
427   /** For disabled tracks, only the tweaked strip is enabled. */
428   return adt->actstrip == strip;
429 }
430
431 /* main call for drawing a single NLA-strip */
432 static void nla_draw_strip(SpaceNla *snla,
433                            AnimData *adt,
434                            NlaTrack *nlt,
435                            NlaStrip *strip,
436                            View2D *v2d,
437                            float yminc,
438                            float ymaxc)
439 {
440   const bool non_solo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
441                          (nlt->flag & NLATRACK_SOLO) == 0);
442   const bool muted = ((nlt->flag & NLATRACK_MUTED) || (strip->flag & NLASTRIP_FLAG_MUTED));
443   float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
444   uint shdr_pos;
445
446   /* get color of strip */
447   nla_strip_get_color_inside(adt, strip, color);
448
449   shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
450   immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
451
452   /* draw extrapolation info first (as backdrop)
453    * - but this should only be drawn if track has some contribution
454    */
455   if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (non_solo == 0)) {
456     /* enable transparency... */
457     GPU_blend(GPU_BLEND_ALPHA);
458
459     switch (strip->extendmode) {
460       /* since this does both sides,
461        * only do the 'before' side, and leave the rest to the next case */
462       case NLASTRIP_EXTEND_HOLD:
463         /* only need to draw here if there's no strip before since
464          * it only applies in such a situation
465          */
466         if (strip->prev == NULL) {
467           /* set the drawing color to the color of the strip, but with very faint alpha */
468           immUniformColor3fvAlpha(color, 0.15f);
469
470           /* draw the rect to the edge of the screen */
471           immRectf(shdr_pos, v2d->cur.xmin, yminc, strip->start, ymaxc);
472         }
473         ATTR_FALLTHROUGH;
474
475       /* this only draws after the strip */
476       case NLASTRIP_EXTEND_HOLD_FORWARD:
477         /* only need to try and draw if the next strip doesn't occur immediately after */
478         if ((strip->next == NULL) || (IS_EQF(strip->next->start, strip->end) == 0)) {
479           /* set the drawing color to the color of the strip, but this time less faint */
480           immUniformColor3fvAlpha(color, 0.3f);
481
482           /* draw the rect to the next strip or the edge of the screen */
483           float x2 = strip->next ? strip->next->start : v2d->cur.xmax;
484           immRectf(shdr_pos, strip->end, yminc, x2, ymaxc);
485         }
486         break;
487     }
488
489     GPU_blend(GPU_BLEND_NONE);
490   }
491
492   /* draw 'inside' of strip itself */
493   if (non_solo == 0 && is_nlastrip_enabled(adt, nlt, strip)) {
494     immUnbindProgram();
495
496     /* strip is in normal track */
497     UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */
498     UI_draw_roundbox_shade_x(
499         &(const rctf){
500             .xmin = strip->start,
501             .xmax = strip->end,
502             .ymin = yminc,
503             .ymax = ymaxc,
504         },
505         true,
506         0.0,
507         0.5,
508         0.1,
509         color);
510
511     /* restore current vertex format & program (roundbox trashes it) */
512     shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
513     immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
514   }
515   else {
516     /* strip is in disabled track - make less visible */
517     immUniformColor3fvAlpha(color, 0.1f);
518
519     GPU_blend(GPU_BLEND_ALPHA);
520     immRectf(shdr_pos, strip->start, yminc, strip->end, ymaxc);
521     GPU_blend(GPU_BLEND_NONE);
522   }
523
524   /* draw strip's control 'curves'
525    * - only if user hasn't hidden them...
526    */
527   if ((snla->flag & SNLA_NOSTRIPCURVES) == 0) {
528     nla_draw_strip_curves(strip, yminc, ymaxc, shdr_pos);
529   }
530
531   immUnbindProgram();
532
533   /* draw markings indicating locations of local markers
534    * (useful for lining up different actions) */
535   if ((snla->flag & SNLA_NOLOCALMARKERS) == 0) {
536     nla_strip_draw_markers(strip, yminc, ymaxc);
537   }
538
539   /* draw strip outline
540    * - color used here is to indicate active vs non-active
541    */
542   if (strip->flag & NLASTRIP_FLAG_ACTIVE) {
543     /* strip should appear 'sunken', so draw a light border around it */
544     color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */
545     color[1] = 1.0f;
546     color[2] = 0.9f;
547   }
548   else {
549     /* strip should appear to stand out, so draw a dark border around it */
550     color[0] = color[1] = color[2] = 0.0f; /* FIXME: or 1.0f ?? */
551   }
552
553   /* draw outline
554    * - dashed-line shader is loaded after this block
555    */
556   if (muted) {
557     /* muted - draw dotted, squarish outline (for simplicity) */
558     shdr_pos = nla_draw_use_dashed_outlines(color, muted);
559     imm_draw_box_wire_2d(shdr_pos, strip->start, yminc, strip->end, ymaxc);
560   }
561   else {
562     /* non-muted - draw solid, rounded outline */
563     UI_draw_roundbox_shade_x(
564         &(const rctf){
565             .xmin = strip->start,
566             .xmax = strip->end,
567             .ymin = yminc,
568             .ymax = ymaxc,
569         },
570         false,
571         0.0,
572         0.0,
573         0.1,
574         color);
575
576     /* restore current vertex format & program (roundbox trashes it) */
577     shdr_pos = nla_draw_use_dashed_outlines(color, muted);
578   }
579
580   /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */
581   if ((strip->type == NLASTRIP_TYPE_CLIP) && strip->repeat > 1.0f) {
582     float repeatLen = (strip->actend - strip->actstart) * strip->scale;
583
584     /* only draw lines for whole-numbered repeats, starting from the first full-repeat
585      * up to the last full repeat (but not if it lies on the end of the strip)
586      */
587     immBeginAtMost(GPU_PRIM_LINES, 2 * floorf(strip->repeat));
588     for (int i = 1; i < strip->repeat; i++) {
589       float repeatPos = strip->start + (repeatLen * i);
590
591       /* don't draw if line would end up on or after the end of the strip */
592       if (repeatPos < strip->end) {
593         immVertex2f(shdr_pos, repeatPos, yminc + 4);
594         immVertex2f(shdr_pos, repeatPos, ymaxc - 4);
595       }
596     }
597     immEnd();
598   }
599   /* or if meta-strip, draw lines delimiting extents of sub-strips
600    * (in same color as outline, if more than 1 exists) */
601   else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) {
602     const float y = (ymaxc - yminc) * 0.5f + yminc;
603
604     /* up to 2 lines per strip */
605     immBeginAtMost(GPU_PRIM_LINES, 4 * BLI_listbase_count(&strip->strips));
606
607     /* only draw first-level of child-strips, but don't draw any lines on the endpoints */
608     LISTBASE_FOREACH (NlaStrip *, cs, &strip->strips) {
609       /* draw start-line if not same as end of previous (and only if not the first strip)
610        * - on upper half of strip
611        */
612       if ((cs->prev) && IS_EQF(cs->prev->end, cs->start) == 0) {
613         immVertex2f(shdr_pos, cs->start, y);
614         immVertex2f(shdr_pos, cs->start, ymaxc);
615       }
616
617       /* draw end-line if not the last strip
618        * - on lower half of strip
619        */
620       if (cs->next) {
621         immVertex2f(shdr_pos, cs->end, yminc);
622         immVertex2f(shdr_pos, cs->end, y);
623       }
624     }
625
626     immEnd();
627   }
628
629   immUnbindProgram();
630 }
631
632 /* add the relevant text to the cache of text-strings to draw in pixelspace */
633 static void nla_draw_strip_text(AnimData *adt,
634                                 NlaTrack *nlt,
635                                 NlaStrip *strip,
636                                 int index,
637                                 View2D *v2d,
638                                 float xminc,
639                                 float xmaxc,
640                                 float yminc,
641                                 float ymaxc)
642 {
643   const bool non_solo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) &&
644                          (nlt->flag & NLATRACK_SOLO) == 0);
645   char str[256];
646   size_t str_len;
647   uchar col[4];
648
649   /* just print the name and the range */
650   if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
651     str_len = BLI_snprintf_rlen(str, sizeof(str), "%d) Temp-Meta", index);
652   }
653   else {
654     str_len = BLI_strncpy_rlen(str, strip->name, sizeof(str));
655   }
656
657   /* set text color - if colors (see above) are light, draw black text, otherwise draw white */
658   if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_TWEAKUSER)) {
659     col[0] = col[1] = col[2] = 0;
660   }
661   else {
662     col[0] = col[1] = col[2] = 255;
663   }
664
665   /* text opacity depends on whether if there's a solo'd track, this isn't it */
666   if (non_solo == 0) {
667     col[3] = 255;
668   }
669   else {
670     col[3] = 128;
671   }
672
673   /* set bounding-box for text
674    * - padding of 2 'units' on either side
675    */
676   /* TODO: make this centered? */
677   rctf rect = {
678       .xmin = xminc,
679       .ymin = yminc,
680       .xmax = xmaxc,
681       .ymax = ymaxc,
682   };
683
684   /* add this string to the cache of texts to draw */
685   UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
686 }
687
688 /**
689  * Add frame extents to cache of text-strings to draw in pixel-space
690  * for now, only used when transforming strips.
691  */
692 static void nla_draw_strip_frames_text(
693     NlaTrack *UNUSED(nlt), NlaStrip *strip, View2D *v2d, float UNUSED(yminc), float ymaxc)
694 {
695   const float ytol = 1.0f; /* small offset to vertical positioning of text, for legibility */
696   const uchar col[4] = {220, 220, 220, 255}; /* light gray */
697   char numstr[32];
698   size_t numstr_len;
699
700   /* Always draw times above the strip, whereas sequencer drew below + above.
701    * However, we should be fine having everything on top, since these tend to be
702    * quite spaced out.
703    * - 1 dp is compromise between lack of precision (ints only, as per sequencer)
704    *   while also preserving some accuracy, since we do use floats
705    */
706   /* start frame */
707   numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%.1f", strip->start);
708   UI_view2d_text_cache_add(v2d, strip->start - 1.0f, ymaxc + ytol, numstr, numstr_len, col);
709
710   /* end frame */
711   numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%.1f", strip->end);
712   UI_view2d_text_cache_add(v2d, strip->end, ymaxc + ytol, numstr, numstr_len, col);
713 }
714
715 /* ---------------------- */
716
717 void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region)
718 {
719   View2D *v2d = &region->v2d;
720   const float pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
721   const float text_margin_x = (8 * UI_DPI_FAC) * pixelx;
722
723   /* build list of channels to draw */
724   ListBase anim_data = {NULL, NULL};
725   int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
726   size_t items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
727
728   /* Update max-extent of channels here (taking into account scrollers):
729    * - this is done to allow the channel list to be scrollable, but must be done here
730    *   to avoid regenerating the list again and/or also because channels list is drawn first
731    * - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for
732    *   start of list offset, and the second is as a correction for the scrollers.
733    */
734   int height = NLACHANNEL_TOT_HEIGHT(ac, items);
735   v2d->tot.ymin = -height;
736
737   /* loop through channels, and set up drawing depending on their type  */
738   float ymax = NLACHANNEL_FIRST_TOP(ac);
739
740   for (bAnimListElem *ale = anim_data.first; ale; ale = ale->next, ymax -= NLACHANNEL_STEP(snla)) {
741     float ymin = ymax - NLACHANNEL_HEIGHT(snla);
742     float ycenter = (ymax + ymin) / 2.0f;
743
744     /* check if visible */
745     if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
746         IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax)) {
747       /* data to draw depends on the type of channel */
748       switch (ale->type) {
749         case ANIMTYPE_NLATRACK: {
750           AnimData *adt = ale->adt;
751           NlaTrack *nlt = (NlaTrack *)ale->data;
752           NlaStrip *strip;
753           int index;
754
755           /* draw each strip in the track (if visible) */
756           for (strip = nlt->strips.first, index = 1; strip; strip = strip->next, index++) {
757             if (BKE_nlastrip_within_bounds(strip, v2d->cur.xmin, v2d->cur.xmax)) {
758               const float xminc = strip->start + text_margin_x;
759               const float xmaxc = strip->end + text_margin_x;
760
761               /* draw the visualization of the strip */
762               nla_draw_strip(snla, adt, nlt, strip, v2d, ymin, ymax);
763
764               /* add the text for this strip to the cache */
765               if (xminc < xmaxc) {
766                 nla_draw_strip_text(adt, nlt, strip, index, v2d, xminc, xmaxc, ymin, ymax);
767               }
768
769               /* if transforming strips (only real reason for temp-metas currently),
770                * add to the cache the frame numbers of the strip's extents
771                */
772               if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
773                 nla_draw_strip_frames_text(nlt, strip, v2d, ymin, ymax);
774               }
775             }
776           }
777           break;
778         }
779         case ANIMTYPE_NLAACTION: {
780           AnimData *adt = ale->adt;
781
782           uint pos = GPU_vertformat_attr_add(
783               immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
784           immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
785
786           /* just draw a semi-shaded rect spanning the width of the viewable area if there's data,
787            * and a second darker rect within which we draw keyframe indicator dots if there's data
788            */
789           GPU_blend(GPU_BLEND_ALPHA);
790
791           /* get colors for drawing */
792           float color[4];
793           nla_action_get_color(adt, ale->data, color);
794           immUniformColor4fv(color);
795
796           /* draw slightly shifted up for greater separation from standard channels,
797            * but also slightly shorter for some more contrast when viewing the strips
798            */
799           immRectf(
800               pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
801
802           /* draw 'embossed' lines above and below the strip for effect */
803           /* white base-lines */
804           GPU_line_width(2.0f);
805           immUniformColor4f(1.0f, 1.0f, 1.0f, 0.3f);
806           immBegin(GPU_PRIM_LINES, 4);
807           immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP);
808           immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP);
809           immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP);
810           immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
811           immEnd();
812
813           /* black top-lines */
814           GPU_line_width(1.0f);
815           immUniformColor3f(0.0f, 0.0f, 0.0f);
816           immBegin(GPU_PRIM_LINES, 4);
817           immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP);
818           immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP);
819           immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP);
820           immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP);
821           immEnd();
822
823           /* TODO: these lines but better --^ */
824
825           immUnbindProgram();
826
827           /* draw keyframes in the action */
828           nla_action_draw_keyframes(
829               v2d, adt, ale->data, ycenter, ymin + NLACHANNEL_SKIP, ymax - NLACHANNEL_SKIP);
830
831           GPU_blend(GPU_BLEND_NONE);
832           break;
833         }
834       }
835     }
836   }
837
838   /* free tempolary channels */
839   ANIM_animdata_freelist(&anim_data);
840 }
841
842 /* *********************************************** */
843 /* Channel List */
844
845 void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *region)
846 {
847   ListBase anim_data = {NULL, NULL};
848   bAnimListElem *ale;
849   int filter;
850
851   SpaceNla *snla = (SpaceNla *)ac->sl;
852   View2D *v2d = &region->v2d;
853   size_t items;
854
855   /* build list of channels to draw */
856   filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
857   items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
858
859   /* Update max-extent of channels here (taking into account scrollers):
860    * - this is done to allow the channel list to be scrollable, but must be done here
861    *   to avoid regenerating the list again and/or also because channels list is drawn first
862    * - offset of NLACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for
863    *  start of list offset, and the second is as a correction for the scrollers.
864    */
865   int height = NLACHANNEL_TOT_HEIGHT(ac, items);
866   v2d->tot.ymin = -height;
867
868   /* need to do a view-sync here, so that the keys area doesn't jump around
869    * (it must copy this) */
870   UI_view2d_sync(NULL, ac->area, v2d, V2D_LOCK_COPY);
871
872   /* draw channels */
873   { /* first pass: just the standard GL-drawing for backdrop + text */
874     size_t channel_index = 0;
875     float ymax = NLACHANNEL_FIRST_TOP(ac);
876
877     for (ale = anim_data.first; ale;
878          ale = ale->next, ymax -= NLACHANNEL_STEP(snla), channel_index++) {
879       float ymin = ymax - NLACHANNEL_HEIGHT(snla);
880
881       /* check if visible */
882       if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
883           IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax)) {
884         /* draw all channels using standard channel-drawing API */
885         ANIM_channel_draw(ac, ale, ymin, ymax, channel_index);
886       }
887     }
888   }
889   { /* second pass: UI widgets */
890     uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS);
891     size_t channel_index = 0;
892     float ymax = NLACHANNEL_FIRST_TOP(ac);
893
894     /* set blending again, as may not be set in previous step */
895     GPU_blend(GPU_BLEND_ALPHA);
896
897     /* loop through channels, and set up drawing depending on their type  */
898     for (ale = anim_data.first; ale;
899          ale = ale->next, ymax -= NLACHANNEL_STEP(snla), channel_index++) {
900       float ymin = ymax - NLACHANNEL_HEIGHT(snla);
901
902       /* check if visible */
903       if (IN_RANGE(ymin, v2d->cur.ymin, v2d->cur.ymax) ||
904           IN_RANGE(ymax, v2d->cur.ymin, v2d->cur.ymax)) {
905         /* draw all channels using standard channel-drawing API */
906         rctf channel_rect;
907         BLI_rctf_init(&channel_rect, 0, v2d->cur.xmax, ymin, ymax);
908         ANIM_channel_draw_widgets(C, ac, ale, block, &channel_rect, channel_index);
909       }
910     }
911
912     UI_block_end(C, block);
913     UI_block_draw(C, block);
914
915     GPU_blend(GPU_BLEND_NONE);
916   }
917
918   /* free temporary channels */
919   ANIM_animdata_freelist(&anim_data);
920 }
921
922 /* *********************************************** */