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