2024-08-12 15:11:25 +00:00
|
|
|
#define TIMELINE_ICON ICON_MDI_CHART_TIMELINE
|
|
|
|
#define TIMELINE_TITLE "Timeline " TIMELINE_ICON
|
|
|
|
|
|
|
|
EDITOR_BIND(timeline, "held(CTRL)&down(3)", { ui_show(TIMELINE_TITLE, ui_visible(TIMELINE_TITLE) ^ true); });
|
|
|
|
|
|
|
|
int ui_tween(const char *label, tween_t *t) {
|
|
|
|
if( ui_filter && ui_filter[0] ) if( !strstr(label, ui_filter) ) return 0;
|
|
|
|
|
|
|
|
int expand_keys = label[0] == '!'; label += expand_keys;
|
|
|
|
const char *id = label;
|
|
|
|
if( strchr(id, '@') ) *strchr((char*)(id = (const char*)va("%s", label)), '@') = '\0';
|
|
|
|
|
|
|
|
enum { LABEL_SPACING = 250 };
|
|
|
|
enum { ROUNDING = 0 };
|
|
|
|
enum { THICKNESS = 1 };
|
|
|
|
enum { PIXELS_PER_SECOND = 60 };
|
|
|
|
enum { KEY_WIDTH = 5, KEY_HEIGHT = 5 };
|
|
|
|
enum { TIMELINE_HEIGHT = 25 };
|
|
|
|
enum { MARKER1_HEIGHT = 5, MARKER10_HEIGHT = 20, MARKER5_HEIGHT = (MARKER1_HEIGHT + MARKER10_HEIGHT) / 2 };
|
|
|
|
unsigned base_color = WHITE;
|
|
|
|
unsigned time_color = YELLOW;
|
|
|
|
unsigned duration_color = ORANGE;
|
|
|
|
unsigned key_color = GREEN;
|
|
|
|
|
|
|
|
int changed = 0;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
// two rows with height:30 composed of three widgets
|
|
|
|
nk_layout_row_template_begin(ui_ctx, 30);
|
|
|
|
nk_layout_row_template_push_variable(ui_ctx, t->duration * PIXELS_PER_SECOND); // min 80px. can grow
|
|
|
|
nk_layout_row_template_end(ui_ctx);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
char *sid = va("%s.%d", id, 0);
|
|
|
|
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
|
|
|
|
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
|
|
|
|
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
|
|
|
|
|
|
|
|
ui_label(label);
|
|
|
|
|
|
|
|
struct nk_command_buffer *canvas = nk_window_get_canvas(ui_ctx);
|
|
|
|
struct nk_rect bounds; nk_layout_peek(&bounds, ui_ctx);
|
|
|
|
bounds.y -= 30;
|
|
|
|
|
|
|
|
struct nk_rect baseline = bounds; baseline.y += 30/2;
|
|
|
|
baseline.x += LABEL_SPACING;
|
|
|
|
baseline.w -= LABEL_SPACING;
|
|
|
|
|
|
|
|
// tween duration
|
|
|
|
{
|
|
|
|
struct nk_rect pos = baseline;
|
|
|
|
pos.w = pos.x + t->duration * PIXELS_PER_SECOND;
|
|
|
|
pos.y -= TIMELINE_HEIGHT/2;
|
|
|
|
pos.h = TIMELINE_HEIGHT;
|
|
|
|
nk_stroke_rect(canvas, pos, ROUNDING, THICKNESS*2, AS_NKCOLOR(duration_color));
|
|
|
|
}
|
|
|
|
|
|
|
|
// tween ranges
|
|
|
|
for(int i = 0, end = array_count(t->keyframes) - 1; i < end; ++i) {
|
|
|
|
tween_keyframe_t *k = t->keyframes + i;
|
|
|
|
tween_keyframe_t *next = k + 1;
|
|
|
|
|
|
|
|
struct nk_rect pos = baseline;
|
|
|
|
pos.x += k->t * PIXELS_PER_SECOND;
|
|
|
|
pos.w = (next->t - k->t) * PIXELS_PER_SECOND;
|
|
|
|
pos.y -= TIMELINE_HEIGHT/2;
|
|
|
|
pos.h = TIMELINE_HEIGHT;
|
|
|
|
|
|
|
|
char *sid = va("%s.%d", id, i);
|
|
|
|
uint64_t hash = 14695981039346656037ULL, mult = 0x100000001b3ULL;
|
|
|
|
for(int i = 0; sid[i]; ++i) hash = (hash ^ sid[i]) * mult;
|
|
|
|
ui_hue = (hash & 0x3F) / (float)0x3F; ui_hue += !ui_hue;
|
|
|
|
|
|
|
|
struct nk_color c = nk_hsva_f(ui_hue, 0.75f, 0.8f, ui_alpha);
|
|
|
|
nk_fill_rect(canvas, pos, ROUNDING, k->ease == EASE_ZERO ? AS_NKCOLOR(0) : c); // AS_NKCOLOR(track_color));
|
|
|
|
}
|
|
|
|
|
|
|
|
// horizontal line
|
|
|
|
nk_stroke_line(canvas, baseline.x, baseline.y, baseline.x+baseline.w,baseline.y, THICKNESS, AS_NKCOLOR(base_color));
|
|
|
|
|
|
|
|
// unit, 5-unit and 10-unit markers
|
|
|
|
for( float i = 0, j = 0; i < baseline.w; i += PIXELS_PER_SECOND/10, ++j ) {
|
|
|
|
int len = !((int)j%10) ? MARKER10_HEIGHT : !((int)j%5) ? MARKER5_HEIGHT : MARKER1_HEIGHT;
|
|
|
|
nk_stroke_line(canvas, baseline.x+i, baseline.y-len, baseline.x+i, baseline.y+len, THICKNESS, AS_NKCOLOR(base_color));
|
|
|
|
}
|
|
|
|
|
|
|
|
// time marker
|
|
|
|
float px = t->time * PIXELS_PER_SECOND;
|
|
|
|
nk_stroke_line(canvas, baseline.x+px, bounds.y, baseline.x+px, bounds.y+bounds.h, THICKNESS*2, AS_NKCOLOR(time_color));
|
|
|
|
nk_draw_symbol(canvas, NK_SYMBOL_TRIANGLE_DOWN, ((struct nk_rect){ baseline.x+px-4,bounds.y-4-8,8,8}), /*bg*/AS_NKCOLOR(0), /*fg*/AS_NKCOLOR(time_color), 0.f/*border_width*/, ui_ctx->style.font);
|
|
|
|
|
|
|
|
// key markers
|
|
|
|
for each_array_ptr(t->keyframes, tween_keyframe_t, k) {
|
|
|
|
struct nk_rect pos = baseline;
|
|
|
|
pos.x += k->t * PIXELS_PER_SECOND;
|
|
|
|
|
|
|
|
vec2 romboid[] = {
|
|
|
|
{pos.x-KEY_WIDTH,pos.y}, {pos.x,pos.y-KEY_HEIGHT},
|
|
|
|
{pos.x+KEY_WIDTH,pos.y}, {pos.x,pos.y+KEY_HEIGHT}
|
|
|
|
};
|
|
|
|
|
|
|
|
nk_fill_polygon(canvas, (float*)romboid, countof(romboid), AS_NKCOLOR(key_color));
|
|
|
|
}
|
|
|
|
|
|
|
|
// keys ui
|
|
|
|
if( expand_keys )
|
|
|
|
for(int i = 0, end = array_count(t->keyframes); i < end; ++i) {
|
|
|
|
tween_keyframe_t *k = t->keyframes + i;
|
|
|
|
if( ui_collapse(va("Key %d", i), va("%s.%d", id, i))) {
|
|
|
|
changed |= ui_float("Time", &k->t);
|
|
|
|
changed |= ui_float3("Value", &k->v.x);
|
|
|
|
changed |= ui_list("Ease", ease_enums(), EASE_NUM, &k->ease );
|
|
|
|
ui_collapse_end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
}
|
|
|
|
|
|
|
|
tween_t* rand_tween() {
|
|
|
|
tween_t demo = tween();
|
|
|
|
int num_keys = randi(2,8);
|
|
|
|
double t = 0;
|
|
|
|
for( int i = 0; i < num_keys; ++i) {
|
|
|
|
tween_setkey(&demo, t, scale3(vec3(randf(),randf(),randf()),randi(-5,5)), randi(0,EASE_NUM) );
|
|
|
|
t += randi(1,5) / ((float)(1 << randi(0,2)));
|
|
|
|
}
|
|
|
|
tween_t *p = CALLOC(1, sizeof(tween_t));
|
|
|
|
memcpy(p, &demo, sizeof(tween_t));
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
|
|
|
int editor_timeline(int window_mode) {
|
|
|
|
static array(tween_t*) tweens = 0;
|
|
|
|
|
|
|
|
do_once {
|
|
|
|
array_push(tweens, rand_tween());
|
|
|
|
}
|
|
|
|
|
|
|
|
if( editor.t == 0 )
|
|
|
|
for each_array(tweens, tween_t*,t) {
|
|
|
|
tween_reset(t);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
for each_array(tweens, tween_t*,t) {
|
|
|
|
tween_update(t, editor.dt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *selected = NULL;
|
|
|
|
if( editor_begin(TIMELINE_TITLE, window_mode) ) {
|
|
|
|
|
|
|
|
int choice = ui_toolbar(ICON_MDI_PLUS ";" ICON_MDI_MINUS );
|
|
|
|
if( choice == 1 ) array_push(tweens, rand_tween());
|
|
|
|
if( choice == 2 && selected ) {
|
|
|
|
int target = -1;
|
|
|
|
for( int i = 0, end = array_count(tweens); i < end; ++i ) if( tweens[i] == selected ) { target = i; break; }
|
|
|
|
if( target >= 0 ) { array_erase_slow(tweens, target); selected = NULL; }
|
|
|
|
}
|
|
|
|
|
|
|
|
for each_array(tweens, tween_t*,t) {
|
|
|
|
ui_tween(va("%s%p@%05.2fs Value: %s", t == selected ? "!":"", t, t->time, ftoa3(t->result)), t);
|
|
|
|
if(ui_label_icon_clicked_L.x) selected = (t != selected) ? t : NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
editor_end(window_mode);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
AUTORUN {
|
|
|
|
array_push(editor.subeditors, editor_timeline);
|
|
|
|
}
|