paint: improve timeline skinning

This commit is contained in:
luboslenco
2026-07-18 12:09:44 +02:00
parent ee77600676
commit 09086a000e
7 changed files with 100 additions and 41 deletions
+10 -15
View File
@@ -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);
}
+54
View File
@@ -4,7 +4,9 @@
#include "engine.h"
#include "iron_array.h"
#include "iron_map.h"
#include "iron_obj.h"
#include "iron_ui.h"
#include <stdlib.h>
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);
}
}
+4 -1
View File
@@ -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);
+6
View File
@@ -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);
+2 -2
View File
@@ -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
+23 -11
View File
@@ -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 *)) {
+1 -12
View File
@@ -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);
}