diff --git a/paint/shaders/layer_tween.kong b/paint/shaders/layer_tween.kong new file mode 100644 index 00000000..5aa665bf --- /dev/null +++ b/paint/shaders/layer_tween.kong @@ -0,0 +1,42 @@ +#[set(everything)] +const constants: { + factor: float; +}; + +#[set(everything)] +const sampler_linear: sampler; + +#[set(everything)] +const tex0: tex2d; + +#[set(everything)] +const tex1: tex2d; + +struct vert_in { + pos: float2; +} + +struct vert_out { + pos: float4; + tex: float2; +} + +fun layer_tween_vert(input: vert_in): vert_out { + var output: vert_out; + output.tex = input.pos.xy * 0.5 + 0.5; + output.tex.y = 1.0 - output.tex.y; + output.pos = float4(input.pos.xy, 0.0, 1.0); + return output; +} + +fun layer_tween_frag(input: vert_out): float4 { + var col0: float4 = sample_lod(tex0, sampler_linear, input.tex, 0.0); + var col1: float4 = sample_lod(tex1, sampler_linear, input.tex, 0.0); + return lerp4(col0, col1, constants.factor); +} + +#[pipe] +struct pipe { + vertex = layer_tween_vert; + fragment = layer_tween_frag; +} diff --git a/paint/sources/io/import_legacy.c b/paint/sources/io/import_legacy.c index 5b45c01a..5ab86221 100644 --- a/paint/sources/io/import_legacy.c +++ b/paint/sources/io/import_legacy.c @@ -252,6 +252,7 @@ project_t *import_arm_from_map_to_arm(any_map_t *old) { d->path_points_world = any_map_get(old, "path_points_world"); d->path_points_camera = any_map_get(old, "path_points_camera"); d->path_points_parent = any_map_get(old, "path_points_parent"); + d->tween = armpack_map_get_i32(old, "tween") > 0; any_array_push(project->timeline_layers, d); } } diff --git a/paint/sources/types.h b/paint/sources/types.h index bef6c23f..d40d3a67 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -631,6 +631,7 @@ typedef struct timeline_layer_keyframe_data { struct f32_array *path_points_world; struct f32_array *path_points_camera; struct i32_array *path_points_parent; + bool tween; } timeline_layer_keyframe_data_t; typedef struct timeline_mesh_keyframe_data { diff --git a/paint/sources/ui/tab_scripts.c b/paint/sources/ui/tab_scripts.c index 4bb2307a..bec4d1fc 100644 --- a/paint/sources/ui/tab_scripts.c +++ b/paint/sources/ui/tab_scripts.c @@ -13,6 +13,10 @@ void tab_scripts_prepare() { string_array_push(g_project->script_datas, "void main() {\n \n}\n"); string_array_push(g_project->script_names, "main.c"); } + // A script may have been removed + if (tab_scripts_selected >= g_project->script_datas->length) { + tab_scripts_selected = g_project->script_datas->length - 1; + } } char *tab_scripts_get() { diff --git a/paint/sources/ui/tab_timeline.c b/paint/sources/ui/tab_timeline.c index a1aaf8f9..b19914c3 100644 --- a/paint/sources/ui/tab_timeline.c +++ b/paint/sources/ui/tab_timeline.c @@ -11,6 +11,7 @@ typedef struct { f32_array_t *path_points_world; f32_array_t *path_points_camera; i32_array_t *path_points_parent; + bool tween; } tab_timeline_keyframe_t; typedef struct { @@ -42,13 +43,13 @@ static i32 tab_timeline_last_frame = 0; static any_array_t *tab_timeline_mesh_keyframes = NULL; static any_array_t *tab_timeline_mesh_origins = NULL; -static i32 tab_timeline_pending_from = -1; -static i32 tab_timeline_pending_to = -1; -static i32 tab_timeline_pending_kf_frame = -1; -static i32 tab_timeline_pending_kf_layer = -1; -static i32 tab_timeline_pending_rm_frame = -1; -static i32 tab_timeline_pending_rm_layer = -1; - +static i32 tab_timeline_pending_from = -1; +static i32 tab_timeline_pending_to = -1; +static i32 tab_timeline_pending_kf_frame = -1; +static i32 tab_timeline_pending_kf_layer = -1; +static i32 tab_timeline_pending_rm_frame = -1; +static i32 tab_timeline_pending_rm_layer = -1; +static f32 tab_timeline_pending_tween_f = -1.0f; static i32 tab_timeline_pending_mesh_add_frame = -1; static i32 tab_timeline_pending_mesh_add_index = -1; static i32 tab_timeline_pending_mesh_rm_frame = -1; @@ -58,6 +59,11 @@ static f64 tab_timeline_last_click_time = 0.0; static i32 tab_timeline_last_click_frame = -1; static i32 tab_timeline_last_click_row = -1; +static gpu_pipeline_t *tab_timeline_tween_pipe = NULL; +static i32 tab_timeline_tween_tex0 = 0; +static i32 tab_timeline_tween_tex1 = 0; +static i32 tab_timeline_tween_factor = 0; + static void tab_timeline_copy_tex(gpu_texture_t *dst, gpu_texture_t *src) { draw_begin(dst, true, 0x00000000); draw_set_pipeline(pipes_copy); @@ -118,6 +124,50 @@ static i32 tab_timeline_find_origin(i32 layer_index) { return -1; } +static i32 tab_timeline_find_next_keyframe(i32 frame, i32 layer_index) { + i32 best = -1; + i32 best_frame = tab_timeline_max_frames + 1; + for (i32 i = 0; i < tab_timeline_keyframes->length; i++) { + tab_timeline_keyframe_t *kf = tab_timeline_keyframes->buffer[i]; + if (kf->layer_index == layer_index && kf->frame > frame && kf->frame < best_frame) { + best_frame = kf->frame; + best = i; + } + } + return best; +} + +static void tab_timeline_init_tween_pipe() { + if (tab_timeline_tween_pipe != NULL) { + return; + } + tab_timeline_tween_pipe = gpu_create_pipeline(); + gc_root(tab_timeline_tween_pipe); + tab_timeline_tween_pipe->vertex_shader = sys_get_shader("layer_tween.vert"); + tab_timeline_tween_pipe->fragment_shader = sys_get_shader("layer_tween.frag"); + gpu_vertex_structure_t *vs = GC_ALLOC_INIT(gpu_vertex_structure_t, {0}); + gpu_vertex_structure_add(vs, "pos", GPU_VERTEX_DATA_F32_2X); + tab_timeline_tween_pipe->input_layout = vs; + gpu_pipeline_compile(tab_timeline_tween_pipe); + tab_timeline_tween_tex0 = 0; + tab_timeline_tween_tex1 = 1; + pipes_offset = 0; + tab_timeline_tween_factor = pipes_get_constant_location("float"); +} + +static void tab_timeline_tween_tex(gpu_texture_t *dst, gpu_texture_t *from, gpu_texture_t *to, f32 t) { + tab_timeline_init_tween_pipe(); + _gpu_begin(dst, NULL, NULL, GPU_CLEAR_NONE, 0, 0.0); + gpu_set_pipeline(tab_timeline_tween_pipe); + gpu_set_texture(tab_timeline_tween_tex0, from); + gpu_set_texture(tab_timeline_tween_tex1, to); + gpu_set_float(tab_timeline_tween_factor, t); + gpu_set_vertex_buffer(const_data_screen_aligned_vb); + gpu_set_index_buffer(const_data_screen_aligned_ib); + gpu_draw(); + gpu_end(); +} + static void tab_timeline_save_origins() { gpu_texture_format_t fmt = tab_timeline_tex_format(); i32 w = config_get_texture_res_x(); @@ -217,6 +267,68 @@ static void tab_timeline_load_from_keyframes(i32 frame) { } } +static void tab_timeline_tween_from_keyframes(f32 frame_f) { + bool any = false; + i32 frame_i = (i32)frame_f; + for (i32 li = 0; li < project_layers->length; li++) { + slot_layer_t *l = project_layers->buffer[li]; + if (!slot_layer_is_layer(l)) { + continue; + } + i32 nxt_kfi = tab_timeline_find_next_keyframe(frame_i, li); + if (nxt_kfi < 0) { + continue; + } + tab_timeline_keyframe_t *nxt = tab_timeline_keyframes->buffer[nxt_kfi]; + if (!nxt->tween) { + continue; + } + + gpu_texture_t *from_texpaint = NULL; + gpu_texture_t *from_texpaint_nor = NULL; + gpu_texture_t *from_texpaint_pack = NULL; + i32 from_frame = 0; + i32 act_kfi = tab_timeline_find_active_keyframe(frame_i, li); + if (act_kfi >= 0) { + tab_timeline_keyframe_t *act = tab_timeline_keyframes->buffer[act_kfi]; + from_texpaint = act->texpaint; + from_texpaint_nor = act->texpaint_nor; + from_texpaint_pack = act->texpaint_pack; + from_frame = act->frame; + } + else { + i32 oi = tab_timeline_find_origin(li); + if (oi < 0) { + continue; + } + tab_timeline_origin_t *o = tab_timeline_origins->buffer[oi]; + from_texpaint = o->texpaint; + from_texpaint_nor = o->texpaint_nor; + from_texpaint_pack = o->texpaint_pack; + from_frame = 0; + } + + f32 t = (frame_f - (f32)from_frame) / (f32)(nxt->frame - from_frame); + tab_timeline_tween_tex(l->texpaint, from_texpaint, nxt->texpaint, t); + tab_timeline_tween_tex(l->texpaint_nor, from_texpaint_nor, nxt->texpaint_nor, t); + tab_timeline_tween_tex(l->texpaint_pack, from_texpaint_pack, nxt->texpaint_pack, t); + any = true; + } + if (any) { + g_context->ddirty = 2; + g_context->layers_preview_dirty = true; + } +} + +static void tab_timeline_tween_on_next_frame(void *_) { + f32 frame_f = tab_timeline_pending_tween_f; + tab_timeline_pending_tween_f = -1.0f; + if (frame_f < 0.0f) { + return; + } + tab_timeline_tween_from_keyframes(frame_f); +} + static i32 tab_timeline_find_mesh_keyframe(i32 frame, i32 mesh_index) { for (i32 i = 0; i < tab_timeline_mesh_keyframes->length; i++) { tab_timeline_mesh_keyframe_t *kf = tab_timeline_mesh_keyframes->buffer[i]; @@ -365,8 +477,11 @@ static void tab_timeline_frame_change_on_next_frame(void *_) { tab_timeline_pending_from = -1; tab_timeline_pending_to = -1; - from == 0 ? tab_timeline_save_origins() : tab_timeline_save_to_keyframes(from); + if (!tab_timeline_playing) { + from == 0 ? tab_timeline_save_origins() : tab_timeline_save_to_keyframes(from); + } to == 0 ? tab_timeline_load_origins() : tab_timeline_load_from_keyframes(to); + tab_timeline_tween_from_keyframes((f32)to); if (!tab_timeline_playing) { from == 0 ? tab_timeline_save_mesh_origins() : tab_timeline_save_mesh_to_keyframes(from); to == 0 ? tab_timeline_load_mesh_origins() : tab_timeline_load_mesh_from_keyframes((float)to); @@ -563,6 +678,7 @@ void tab_timeline_export(project_t *raw) { .path_points_world = kf->path_points_world, .path_points_camera = kf->path_points_camera, .path_points_parent = kf->path_points_parent, + .tween = kf->tween, }); any_array_push(layers, d); } @@ -619,6 +735,7 @@ void tab_timeline_import(project_t *raw) { kf->path_points_world = d->path_points_world; kf->path_points_camera = d->path_points_camera; kf->path_points_parent = d->path_points_parent; + kf->tween = d->tween; any_array_push(tab_timeline_keyframes, kf); } } @@ -753,16 +870,23 @@ void tab_timeline_player_update() { tab_timeline_load_from_keyframes(frame_i); tab_timeline_run_frame_scripts(frame_i); } + + if (tab_timeline_keyframes != NULL) { + tab_timeline_tween_from_keyframes(frame_f); + } } void tab_timeline_draw_frame_context_menu() { i32 layer_count = project_layers->length; bool is_mesh = tab_timeline_selected_row >= layer_count; bool has_kf; - i32 mesh_kfi = -1; + i32 mesh_kfi = -1; + i32 layer_kfi = -1; if (!is_mesh) { - has_kf = tab_timeline_keyframes != NULL && tab_timeline_selected_frame > 0 && - tab_timeline_find_keyframe(tab_timeline_selected_frame, tab_timeline_selected_row) >= 0; + layer_kfi = tab_timeline_keyframes != NULL && tab_timeline_selected_frame > 0 + ? tab_timeline_find_keyframe(tab_timeline_selected_frame, tab_timeline_selected_row) + : -1; + has_kf = layer_kfi >= 0; } else { i32 mi = tab_timeline_selected_row - layer_count; @@ -775,17 +899,21 @@ void tab_timeline_draw_frame_context_menu() { tab_timeline_edit_script(tab_timeline_selected_row, tab_timeline_selected_frame); } - ui->enabled = has_kf && is_mesh; - ui_handle_t *h_tween = ui_handle(__ID__); - h_tween->b = false; - tab_timeline_mesh_keyframe_t *kf = NULL; + ui->enabled = has_kf; + ui_handle_t *h_tween = ui_handle(__ID__); + h_tween->b = false; if (ui->enabled) { - kf = tab_timeline_mesh_keyframes->buffer[mesh_kfi]; - h_tween->b = kf->tween; + h_tween->b = is_mesh ? ((tab_timeline_mesh_keyframe_t *)tab_timeline_mesh_keyframes->buffer[mesh_kfi])->tween + : ((tab_timeline_keyframe_t *)tab_timeline_keyframes->buffer[layer_kfi])->tween; } ui_check(h_tween, tr("Tween"), ""); if (ui->enabled && h_tween->changed) { - kf->tween = h_tween->b; + if (is_mesh) { + ((tab_timeline_mesh_keyframe_t *)tab_timeline_mesh_keyframes->buffer[mesh_kfi])->tween = h_tween->b; + } + else { + ((tab_timeline_keyframe_t *)tab_timeline_keyframes->buffer[layer_kfi])->tween = h_tween->b; + } ui_menu_keep_open = true; } @@ -909,6 +1037,10 @@ void tab_timeline_draw(ui_handle_t *htab) { if (tab_timeline_mesh_keyframes != NULL) { tab_timeline_load_mesh_from_keyframes(frame_f); } + if (tab_timeline_keyframes != NULL && tab_timeline_pending_tween_f < 0.0f) { + tab_timeline_pending_tween_f = frame_f; + sys_notify_on_next_frame(&tab_timeline_tween_on_next_frame, NULL); + } } // Frame number labels every 5 frames diff --git a/paint/sources/util/util_encode.c b/paint/sources/util/util_encode.c index 567023ee..dd2b55f9 100644 --- a/paint/sources/util/util_encode.c +++ b/paint/sources/util/util_encode.c @@ -481,7 +481,7 @@ buffer_t *util_encode_project(project_t *raw) { if (raw->timeline_layers != NULL) { armpack_encode_array(raw->timeline_layers->length); for (i32 i = 0; i < raw->timeline_layers->length; ++i) { - armpack_encode_map(9); + armpack_encode_map(10); armpack_encode_string("frame"); armpack_encode_i32(raw->timeline_layers->buffer[i]->frame); armpack_encode_string("layer_index"); @@ -500,6 +500,8 @@ buffer_t *util_encode_project(project_t *raw) { armpack_encode_array_f32(raw->timeline_layers->buffer[i]->path_points_camera); armpack_encode_string("path_points_parent"); armpack_encode_array_i32(raw->timeline_layers->buffer[i]->path_points_parent); + armpack_encode_string("tween"); + armpack_encode_bool(raw->timeline_layers->buffer[i]->tween); } } else {