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