From 09086a000e2f9bb701b47beed129042f1a22e185 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Sat, 18 Jul 2026 12:09:44 +0200 Subject: [PATCH] paint: improve timeline skinning --- paint/assets/plugins/make_tilesheet.c | 25 +++++-------- paint/plugins/plugins.c | 54 +++++++++++++++++++++++++++ paint/sources/functions.h | 5 ++- paint/sources/io/import_mesh.c | 6 +++ paint/sources/minic_api.c | 4 +- paint/sources/project.c | 34 +++++++++++------ paint/sources/ui/tab_timeline.c | 13 +------ 7 files changed, 100 insertions(+), 41 deletions(-) diff --git a/paint/assets/plugins/make_tilesheet.c b/paint/assets/plugins/make_tilesheet.c index 10507596..783b0cba 100644 --- a/paint/assets/plugins/make_tilesheet.c +++ b/paint/assets/plugins/make_tilesheet.c @@ -5,7 +5,6 @@ ui_handle_t *h0; ui_handle_t *h1; ui_handle_t *h2; ui_handle_t *h3; -ui_handle_t *h4; void *tilesheet; int baking; @@ -17,8 +16,7 @@ int frame; int col; int row; int wait; -int skinning; -int reimported; +int reskinned; void on_update() { if (!baking) { @@ -28,11 +26,13 @@ void on_update() { iron_delay_idle_sleep(); if (frame < frames) { - if (skinning && !reimported) { - project_reimport_mesh_skinned(frame); - reimported = 1; - wait = 2; // Wait for re-import and re-render - return; + if (!reskinned) { + reskinned = 1; + // Pose the mesh at this frame, only meshes imported with skinning applied + if (project_reskin_mesh(frame)) { + wait = 2; // Wait for re-render + return; + } } if (wait > 0) { @@ -50,7 +50,7 @@ void on_update() { col = 0; row = row + 1; } - reimported = 0; + reskinned = 0; } else { viewport_save_texture(tilesheet); @@ -69,13 +69,11 @@ void on_ui() { ui_slider(h1, "Tile Size", 0, 512, true, 1, true, UI_ALIGN_LEFT, true); ui_slider(h2, "Columns", 0, 64, true, 1, true, UI_ALIGN_LEFT, true); ui_slider(h3, "Frames", 0, 1024, true, 1, true, UI_ALIGN_LEFT, true); - ui_check(h4, "Skinning", ""); if (ui_button("Bake", UI_ALIGN_CENTER, "") && !baking) { tile_size = h1->f; columns = h2->f; frames = h3->f; - skinning = h4->b; // Square atlas int size = tile_size * columns; @@ -86,7 +84,7 @@ void on_ui() { col = 0; row = 0; wait = 2; - reimported = 0; + reskinned = 0; baking = 1; context_t *c = script_get_context(); c->capturing_screenshot = true; @@ -104,8 +102,6 @@ void main() { h2->f = 8; h3 = ui_handle_create(); h3->f = 64; - h4 = ui_handle_create(); - h4->b = 0; tilesheet = NULL; baking = 0; gc_root(plugin); @@ -113,7 +109,6 @@ void main() { gc_root(h1); gc_root(h2); gc_root(h3); - gc_root(h4); plugin_notify_on_ui(plugin, on_ui); plugin_notify_on_update(plugin, on_update); } diff --git a/paint/plugins/plugins.c b/paint/plugins/plugins.c index 2e6b350c..ecb40fc4 100644 --- a/paint/plugins/plugins.c +++ b/paint/plugins/plugins.c @@ -4,7 +4,9 @@ #include "engine.h" #include "iron_array.h" #include "iron_map.h" +#include "iron_obj.h" #include "iron_ui.h" +#include void *io_svg_parse(char *buf); void *io_exr_parse(char *buf, size_t len); @@ -79,6 +81,57 @@ static void *import_svg(char *path) { return io_svg_parse((char *)b->buffer); } +static buffer_t *plugins_skin_blob = NULL; + +void plugins_skin_data_clear() { + gc_unroot(plugins_skin_blob); + plugins_skin_blob = NULL; +} + +static void plugins_skin_data_set(buffer_t *b) { + plugins_skin_data_clear(); + plugins_skin_blob = b; + gc_root(plugins_skin_blob); +} + +bool plugins_skin_data_exists() { + return plugins_skin_blob != NULL; +} + +static void plugins_free_raw_mesh(raw_mesh_t *raw) { + i16_array_t *arrays[] = {raw->posa, raw->nora, raw->texa, raw->texa1, raw->cola}; + for (int i = 0; i < 5; ++i) { + if (arrays[i] != NULL) { + free(arrays[i]->buffer); + free(arrays[i]); + } + } + if (raw->inda != NULL) { + free(raw->inda->buffer); + free(raw->inda); + } + free(raw->name); + free(raw); +} + +bool plugins_skin_data_apply(int frame, i16_array_t *posa, i16_array_t *nora, float *scale_pos) { + if (plugins_skin_blob == NULL) { + return false; + } + + raw_mesh_t *raw = io_gltf_parse_skinned((char *)plugins_skin_blob->buffer, plugins_skin_blob->length, NULL, frame); + if (raw == NULL) { + return false; + } + + memcpy(posa->buffer, raw->posa->buffer, posa->length * sizeof(int16_t)); + memcpy(nora->buffer, raw->nora->buffer, nora->length * sizeof(int16_t)); + *scale_pos = raw->scale_pos; + + plugins_free_raw_mesh(raw); + return true; +} + static void *import_gltf_glb(char *path) { buffer_t *b = data_get_blob(path); data_delete_blob(path); @@ -86,6 +139,7 @@ static void *import_gltf_glb(char *path) { return io_gltf_parse((char *)b->buffer, b->length, path); } else { + plugins_skin_data_set(b); return io_gltf_parse_skinned((char *)b->buffer, b->length, path, plugins_skinning_frame); } } diff --git a/paint/sources/functions.h b/paint/sources/functions.h index 4a17c6ab..9bb60047 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -441,7 +441,7 @@ void project_import_mesh(bool replace_existing, void (*done void project_append_mesh(); void project_import_mesh_box(char *path, bool replace_existing, bool clear_layers, bool keep_camera, void (*done)(void)); void project_reimport_mesh(); -void project_reimport_mesh_skinned(int frame); +bool project_reskin_mesh(int frame); void project_unwrap_mesh_box(); void project_import_asset(char *filters, bool hdr_as_envmap); void project_import_swatches(bool replace_existing); @@ -612,6 +612,9 @@ void import_keymap_run(char *path); void tab_plugins_draw(ui_handle_t *htab); void plugin_uv_unwrap_button(); void plugin_uv_unwrap_per_object_button(mesh_object_t *mo); +void plugins_skin_data_clear(); +bool plugins_skin_data_exists(); +bool plugins_skin_data_apply(int frame, i16_array_t *posa, i16_array_t *nora, float *scale_pos); void make_discard_color_id(node_shader_t *kong, char *tex_coord); void make_discard_face(node_shader_t *kong); void make_discard_uv_island(node_shader_t *kong); diff --git a/paint/sources/io/import_mesh.c b/paint/sources/io/import_mesh.c index dbd9a449..dea98ab7 100644 --- a/paint/sources/io/import_mesh.c +++ b/paint/sources/io/import_mesh.c @@ -19,6 +19,12 @@ void import_mesh_run(char *path, bool _clear_layers, bool replace_existing, bool import_mesh_no_reset = keep_camera; g_context->layer_filter = 0; +#ifdef WITH_PLUGINS + if (replace_existing) { + plugins_skin_data_clear(); + } +#endif + char *p = to_lower_case(path); if (ends_with(p, ".obj")) { import_obj_run(path, replace_existing); diff --git a/paint/sources/minic_api.c b/paint/sources/minic_api.c index 54ad1a56..f1ebafd6 100644 --- a/paint/sources/minic_api.c +++ b/paint/sources/minic_api.c @@ -44,7 +44,7 @@ void project_save(bool save_and_quit); char *project_filepath_get(); char *project_basepath_get(); void project_filepath_set(char *s); -void project_reimport_mesh_skinned(i32 frame); +bool project_reskin_mesh(int frame); void context_set_viewport_shader(void *viewport_shader); void context_set_viewport_mode(int mode); void context_set_camera_controls(int i); @@ -1252,7 +1252,7 @@ void minic_register_builtins() { R(gpu_create_render_target, "p(i,i,i)"); R(viewport_capture_screenshot_to, "v(p,f,f,f,f)"); R(viewport_save_texture, "v(p)"); - R(project_reimport_mesh_skinned, "v(i)"); + R(project_reskin_mesh, "b(i)"); R(iron_delay_idle_sleep, "v()"); // json diff --git a/paint/sources/project.c b/paint/sources/project.c index d65beb7d..6f341aed 100644 --- a/paint/sources/project.c +++ b/paint/sources/project.c @@ -376,18 +376,30 @@ void project_reimport_mesh() { } } -void project_reimport_mesh_skinned(int frame) { - extern bool import_mesh_no_scale; - extern bool import_mesh_keep_timeline; - extern int plugins_skinning_frame; - if (g_project->mesh_assets != NULL && g_project->mesh_assets->length > 0 && iron_file_exists(g_project->mesh_assets->buffer[0])) { - plugins_skinning_frame = frame; - import_mesh_no_scale = true; - import_mesh_keep_timeline = true; - import_mesh_run(g_project->mesh_assets->buffer[0], false, true, true); - import_mesh_keep_timeline = false; - plugins_skinning_frame = -1; +bool project_reskin_mesh(int frame) { +#ifdef WITH_PLUGINS + if (!plugins_skin_data_exists()) { + return false; } + + mesh_data_t *md = context_main_object()->data; + vertex_array_t *pos = mesh_data_get_vertex_array(md, "pos"); + vertex_array_t *nor = mesh_data_get_vertex_array(md, "nor"); + if (!plugins_skin_data_apply(frame, pos->values, nor->values, &md->scale_pos)) { + return false; + } + + mesh_data_build_vertices(md->_->vertex_buffer, md->vertex_arrays); + + if (g_context->merged_object != NULL) { + util_mesh_merge(NULL); + } + g_context->ddirty = 4; + render_path_raytrace_ready = false; + return true; +#else + return false; +#endif } void project_unwrap_mesh(raw_mesh_t *mesh, void (*done)(raw_mesh_t *)) { diff --git a/paint/sources/ui/tab_timeline.c b/paint/sources/ui/tab_timeline.c index 8943836c..079f635b 100644 --- a/paint/sources/ui/tab_timeline.c +++ b/paint/sources/ui/tab_timeline.c @@ -35,7 +35,6 @@ i32 tab_timeline_scroll_drag_v = 0; static i32 tab_timeline_max_frames = 200; static i32 tab_timeline_frame_rate = 24; -static bool tab_timeline_skinning = false; static any_array_t *tab_timeline_keyframes = NULL; static any_array_t *tab_timeline_origins = NULL; static i32 tab_timeline_last_frame = 0; @@ -489,9 +488,7 @@ static void tab_timeline_frame_change_on_next_frame(void *_) { to == 0 ? tab_timeline_load_mesh_origins() : tab_timeline_load_mesh_from_keyframes((float)to); } - if (tab_timeline_skinning) { - project_reimport_mesh_skinned(to); - } + project_reskin_mesh(to); } static void tab_timeline_add_keyframe_on_next_frame(void *_) { @@ -946,14 +943,6 @@ void tab_timeline_draw_edit() { ui_menu_keep_open = true; } - ui_handle_t *hskin = ui_handle(__ID__); - hskin->b = tab_timeline_skinning; - ui_check(hskin, tr("Skinning"), ""); - if (hskin->changed) { - tab_timeline_skinning = hskin->b; - ui_menu_keep_open = true; - } - if (ui_menu_button(tr("Clear"), "", ICON_ERASE)) { sys_notify_on_next_frame(&tab_timeline_clear_on_next_frame, NULL); }