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