diff --git a/paint/sources/functions.h b/paint/sources/functions.h index dc48d7fb..aa6598bf 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -460,6 +460,8 @@ context_t *script_get_context(); config_t *script_get_config(); project_t *script_get_project(); object_t *script_get_object(char *s); +slot_material_t *script_get_material(char *s); +string_array_t *script_shape_list(void); object_t *script_shape_add(char *name); object_t *script_object_duplicate(object_t *o); void script_add_trait(char *object, char *trait); @@ -598,6 +600,14 @@ void tab_textures_accept_asset_drop(asset_t *asset); void tab_browser_show_directory(char *directory); void tab_browser_draw(ui_handle_t *htab); mesh_object_t_array_t *util_mesh_get_visible(); +mesh_object_t_array_t *util_mesh_dedup_data(mesh_object_t_array_t *objects); +mesh_object_t_array_t *util_mesh_get_unique_data(); +i32 util_mesh_data_owner(mesh_data_t *data); +bool util_mesh_data_is_shared(mesh_data_t *data); +void util_mesh_sync_scale_world(mesh_data_t *data); +void util_mesh_unshare_data(mesh_object_t *o); +char *util_mesh_link_name(i32 source_index, char *object_name); +bool util_mesh_link_parse(char *name, i32 *source_index, char **object_name); mesh_data_t *util_mesh_data_duplicate(mesh_data_t *source); void util_mesh_merge(mesh_object_t_array_t *paint_objects); void util_mesh_visibility_changed(); @@ -757,10 +767,14 @@ void ui_box_hide(); void tab_meshes_draw(ui_handle_t *htab); void tab_meshes_reset_preview_map(); void tab_meshes_accept_mesh_drop(mesh_object_t *mesh); +void tab_meshes_accept_material_drop(slot_material_t *material); mesh_object_t *tab_meshes_append_shape(char *mesh_name); void tab_meshes_set_override(mesh_object_t *o, i32 mat_index); void tab_meshes_set_override_data(mesh_object_t *o, i32 mat_index, material_data_t *data); i32 tab_meshes_get_override(mesh_object_t *o); +i32 tab_meshes_get_linked_override(mesh_object_t *o); +void tab_meshes_set_linked_override(mesh_object_t *o, i32 mat_index); +void tab_meshes_refresh_overrides(slot_material_t *material); void tab_meshes_on_material_deleted(i32 deleted_index); void tab_meshes_on_material_reordered(i32 old_index, i32 new_index); void tab_meshes_draw_transform_loc(mesh_object_t *o, char *ns); diff --git a/paint/sources/io/export_arm.c b/paint/sources/io/export_arm.c index 2f0da78e..b9521fab 100644 --- a/paint/sources/io/export_arm.c +++ b/paint/sources/io/export_arm.c @@ -1,11 +1,30 @@ #include "../global.h" +static mesh_data_t *export_arm_named_mesh_data(mesh_object_t *p) { + // Mesh data shared by linked duplicates is named after the object holding it + if (string_equals(p->data->name, p->base->name)) { + return p->data; + } + mesh_data_t *renamed = gc_alloc(sizeof(mesh_data_t)); + *renamed = *p->data; + renamed->name = string_copy(p->base->name); + return renamed; +} + +static mesh_data_t *export_arm_linked_mesh_data(mesh_object_t *p, i32 source_index) { + return GC_ALLOC_INIT(mesh_data_t, {.name = util_mesh_link_name(source_index, p->base->name), + .scale_pos = p->data->scale_pos, + .scale_tex = p->data->scale_tex, + .vertex_arrays = any_array_create_from_raw((void *[]){}, 0), + .index_array = u32_array_create(0)}); +} + void export_arm_run_mesh(char *path, mesh_object_t_array_t *paint_objects) { mesh_data_t_array_t *mesh_datas = any_array_create_from_raw((void *[]){}, 0); for (i32 i = 0; i < paint_objects->length; ++i) { mesh_object_t *p = paint_objects->buffer[i]; - any_array_push(mesh_datas, p->data); + any_array_push(mesh_datas, export_arm_named_mesh_data(p)); } scene_t *raw = GC_ALLOC_INIT(scene_t, {.mesh_datas = mesh_datas}); @@ -186,8 +205,9 @@ void export_arm_run_project() { mesh_data_t_array_t *md = any_array_create_from_raw((void *[]){}, 0); for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { - mesh_object_t *p = g_project->_->paint_objects->buffer[i]; - any_array_push(md, p->data); + mesh_object_t *p = g_project->_->paint_objects->buffer[i]; + i32 source = util_mesh_data_owner(p->data); + any_array_push(md, source >= 0 && source < i ? export_arm_linked_mesh_data(p, source) : export_arm_named_mesh_data(p)); } string_array_t *texture_files = export_arm_assets_to_files(g_project->_->filepath, g_project->_->assets); @@ -305,7 +325,7 @@ void export_arm_run_project() { for (i32 i = 0; i < g_project->mesh_parents->length; ++i) { object_t *parent = g_project->_->paint_objects->buffer[i]->base->parent; g_project->mesh_parents->buffer[i] = -1; // No parent - if (parent != NULL && parent != _scene_scene_parent) { + if (parent != NULL && parent != _scene_root) { for (i32 j = 0; j < g_project->mesh_parents->length; ++j) { if (g_project->_->paint_objects->buffer[j]->base == parent) { g_project->mesh_parents->buffer[i] = j; diff --git a/paint/sources/io/import_arm.c b/paint/sources/io/import_arm.c index 999dc342..ac5baa22 100644 --- a/paint/sources/io/import_arm.c +++ b/paint/sources/io/import_arm.c @@ -387,25 +387,49 @@ void import_arm_run_project(char *path) { } } - mesh_data_t *md = mesh_data_create(g_project->mesh_datas->buffer[0]); - md->_->skin_blob = import_arm_get_mesh_skin(0); + mesh_data_t_array_t *mesh_datas = any_array_create_from_raw((void *[]){}, 0); + string_array_t *mesh_names = string_array_create(0); + for (i32 i = 0; i < g_project->mesh_datas->length; ++i) { + mesh_data_t *raw = g_project->mesh_datas->buffer[i]; + i32 source_index; + char *object_name; + if (i > 0 && util_mesh_link_parse(raw->name, &source_index, &object_name)) { + any_array_push(mesh_datas, NULL); + string_array_push(mesh_names, object_name); + } + else { + mesh_data_t *md = mesh_data_create(raw); + md->_->skin_blob = import_arm_get_mesh_skin(i); + any_array_push(mesh_datas, md); + string_array_push(mesh_names, md->name); + } + } + for (i32 i = 1; i < mesh_datas->length; ++i) { + if (mesh_datas->buffer[i] != NULL) { + continue; + } + i32 source_index; + char *object_name; + util_mesh_link_parse(((mesh_data_t *)g_project->mesh_datas->buffer[i])->name, &source_index, &object_name); + bool linked = source_index >= 0 && source_index < mesh_datas->length && mesh_datas->buffer[source_index] != NULL; + mesh_datas->buffer[i] = linked ? mesh_datas->buffer[source_index] : mesh_datas->buffer[0]; + } + + mesh_data_t *md = mesh_datas->buffer[0]; mesh_object_set_data(g_context->paint_object, md); g_context->paint_object->base->transform->scale = (vec4_t){1, 1, 1, 1.0}; transform_build_matrix(g_context->paint_object->base->transform); - g_context->paint_object->base->name = md->name; + g_context->paint_object->base->name = mesh_names->buffer[0]; g_project->_->paint_objects = any_array_create_from_raw( (void *[]){ g_context->paint_object, }, 1); - for (i32 i = 1; i < g_project->mesh_datas->length; ++i) { - mesh_data_t *raw = g_project->mesh_datas->buffer[i]; - mesh_data_t *md = mesh_data_create(raw); - md->_->skin_blob = import_arm_get_mesh_skin(i); - mesh_object_t *object = scene_add_mesh_object(md, g_context->paint_object->material, g_context->paint_object->base); - object->base->name = md->name; + for (i32 i = 1; i < mesh_datas->length; ++i) { + mesh_object_t *object = scene_add_mesh_object(mesh_datas->buffer[i], g_context->paint_object->material, g_context->paint_object->base); + object->base->name = mesh_names->buffer[i]; object->skip_context = "paint"; any_array_push(g_project->_->paint_objects, object); } @@ -663,6 +687,7 @@ void import_arm_run_project(char *path) { object_t *parent = parent_index >= 0 ? g_project->_->paint_objects->buffer[parent_index]->base : NULL; object_set_parent(g_project->_->paint_objects->buffer[i]->base, parent); } + transform_build_matrix(_scene_root->transform); } if (g_project->mesh_physics_shapes != NULL) { diff --git a/paint/sources/sim.c b/paint/sources/sim.c index 0776b883..100bb8e5 100644 --- a/paint/sources/sim.c +++ b/paint/sources/sim.c @@ -86,7 +86,7 @@ mesh_object_t *sim_duplicate_object(mesh_object_t *so) { return NULL; } - mesh_data_t *data = util_mesh_data_duplicate(so->data); + mesh_data_t *data = so->data; mesh_object_t *dup = scene_add_mesh_object(data, so->material, so->base->parent); transform_set_matrix(dup->base->transform, so->base->transform->local); any_array_push(g_project->_->paint_objects, dup); @@ -99,7 +99,6 @@ mesh_object_t *sim_duplicate_object(mesh_object_t *so) { ext = string_copy(_import_mesh_number_ext(++i)); } dup->base->name = string("%s%s", oname, ext); - dup->data->name = dup->base->name; tab_stages_add_object(dup->base->name); // Material override @@ -120,6 +119,8 @@ mesh_object_t *sim_duplicate_object(mesh_object_t *so) { void sim_duplicate() { sim_duplicate_object(g_context->paint_object); + util_mesh_merge(NULL); + g_context->ddirty = 2; } void sim_delete() { diff --git a/paint/sources/ui/tab_meshes.c b/paint/sources/ui/tab_meshes.c index b04be914..a95234d5 100644 --- a/paint/sources/ui/tab_meshes.c +++ b/paint/sources/ui/tab_meshes.c @@ -8,6 +8,23 @@ i32 tab_meshes_mesh_name_edit = -1; any_map_t *tab_meshes_preview_map = NULL; any_map_t *tab_meshes_override_map = NULL; // object uid -> overridden material index +static i32 tab_meshes_material_drop_index = -1; + +bool tab_meshes_search_show = false; +bool tab_meshes_search_focus = false; +ui_handle_t *tab_meshes_search_handle = NULL; + +static bool tab_meshes_slot_hidden(mesh_object_t *o) { + if (!tab_meshes_search_show || tab_meshes_search_handle == NULL) { + return false; + } + char *search = tab_meshes_search_handle->text; + if (search == NULL || string_equals(search, "")) { + return false; + } + return string_index_of(to_lower_case(o->base->name), to_lower_case(search)) < 0; +} + void tab_meshes_set_drag_mesh(mesh_object_t *o, f32 off_x, f32 off_y) { base_drag_off_x = off_x; base_drag_off_y = off_y; @@ -62,6 +79,63 @@ i32 tab_meshes_get_override(mesh_object_t *o) { return v != NULL ? parse_int(v) : -1; } +i32 tab_meshes_get_linked_override(mesh_object_t *o) { + i32 owner = util_mesh_data_owner(o->data); + return tab_meshes_get_override(owner >= 0 ? g_project->_->paint_objects->buffer[owner] : o); +} + +void tab_meshes_set_linked_override(mesh_object_t *o, i32 mat_index) { + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + mesh_object_t *p = g_project->_->paint_objects->buffer[i]; + if (p->data == o->data) { + tab_meshes_set_override(p, mat_index); + } + } +} + +void tab_meshes_accept_material_drop(slot_material_t *material) { + i32 index = tab_meshes_material_drop_index; + tab_meshes_material_drop_index = -1; + if (index < 0 || index >= g_project->_->paint_objects->length) { + return; + } + i32 mat_index = array_index_of(g_project->_->materials, material); + if (mat_index < 0) { + return; + } + mesh_object_t *o = g_project->_->paint_objects->buffer[index]; + if (tab_meshes_get_linked_override(o) == mat_index) { + return; + } + tab_meshes_set_linked_override(o, mat_index); + g_context->ddirty = 2; + g_context->rtdirty = 2; + g_project->mesh_materials = i32_array_create(0); +} + +void tab_meshes_refresh_overrides(slot_material_t *material) { + if (tab_meshes_override_map == NULL) { + return; + } + i32 mat_index = array_index_of(g_project->_->materials, material); + if (mat_index < 0) { + return; + } + material_data_t *data = NULL; + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + mesh_object_t *o = g_project->_->paint_objects->buffer[i]; + if (tab_meshes_get_override(o) != mat_index) { + continue; + } + tab_meshes_set_override_data(o, mat_index, data); + data = o->material; + } + if (data != NULL) { + g_context->ddirty = 2; + g_context->rtdirty = 2; + } +} + void tab_meshes_on_material_deleted(i32 deleted_index) { if (tab_meshes_override_map == NULL) { return; @@ -123,7 +197,9 @@ void tab_meshes_on_material_reordered(i32 old_index, i32 new_index) { } void tab_meshes_draw_context_menu_delete_next_frame(mesh_object_t *o) { - data_delete_mesh(o->data->_->handle); + if (util_mesh_data_owner(o->data) == -1) { + data_delete_mesh(o->data->_->handle); + } mesh_object_remove(o); tab_stages_prune(); g_context->paint_object = context_main_object(); @@ -293,6 +369,15 @@ void tab_meshes_draw_context_menu() { sim_duplicate(); return; } + if (util_mesh_data_is_shared(o->data) && ui_menu_button(tr("Make Unique"), "", ICON_DUPLICATE)) { + util_mesh_unshare_data(o); + util_mesh_merge(NULL); + util_uv_uvmap_cached = false; + util_uv_trianglemap_cached = false; + util_uv_dilatemap_cached = false; + g_context->ddirty = 2; + return; + } if (ui_menu_button(tr("Edit Script"), "", ICON_EDIT)) { tab_timeline_edit_script(g_project->_->layers->length + i, 0); return; @@ -363,10 +448,10 @@ void tab_meshes_draw_context_menu() { } ui_handle_t *hmat = ui_handle(__ID__); - hmat->i = tab_meshes_get_override(o) + 1; // 0 = none + hmat->i = tab_meshes_get_linked_override(o) + 1; // 0 = none ui_combo(hmat, mat_combo, tr("Material"), true, UI_ALIGN_LEFT, false); if (hmat->changed) { - tab_meshes_set_override(o, hmat->i - 1); + tab_meshes_set_linked_override(o, hmat->i - 1); g_context->ddirty = 2; g_context->rtdirty = 2; g_project->mesh_materials = i32_array_create(0); @@ -555,6 +640,9 @@ mesh_object_t *tab_meshes_append_shape(char *mesh_name) { md->_->handle = md->name; mesh_object_t *mo = scene_add_mesh_object(md, g_project->_->paint_objects->buffer[0]->material, NULL); + // The shape stays at the scene root + g_project->mesh_parents = i32_array_create(0); + // Ensure unique name char *ext = ""; i32 n = 0; @@ -575,7 +663,7 @@ mesh_object_t *tab_meshes_append_shape(char *mesh_name) { } static icon_t tab_meshes_mesh_name_to_icon(char *s) { - if (starts_with(s, "box")) + if (starts_with(s, "cube")) return ICON_CUBE; if (starts_with(s, "cone")) return ICON_CONE; @@ -609,33 +697,10 @@ void tab_meshes_draw_import() { } static vec4_t aabb_center(mesh_data_t *raw) { - vec4_t aabb_min = (vec4_t){-0.01, -0.01, -0.01, 0.0}; - vec4_t aabb_max = (vec4_t){0.01, 0.01, 0.01, 0.0}; - i32 i = 0; - vertex_array_t *positions = mesh_data_get_vertex_array(raw, "pos"); - while (i < positions->values->length) { - if (positions->values->buffer[i] > aabb_max.x) { - aabb_max.x = positions->values->buffer[i]; - } - if (positions->values->buffer[i + 1] > aabb_max.y) { - aabb_max.y = positions->values->buffer[i + 1]; - } - if (positions->values->buffer[i + 2] > aabb_max.z) { - aabb_max.z = positions->values->buffer[i + 2]; - } - if (positions->values->buffer[i] < aabb_min.x) { - aabb_min.x = positions->values->buffer[i]; - } - if (positions->values->buffer[i + 1] < aabb_min.y) { - aabb_min.y = positions->values->buffer[i + 1]; - } - if (positions->values->buffer[i + 2] < aabb_min.z) { - aabb_min.z = positions->values->buffer[i + 2]; - } - i += 4; - } - f32 f = raw->scale_pos / 32767.0f; - return (vec4_t){(aabb_min.x + aabb_max.x) / 2.0f * f, (aabb_min.y + aabb_max.y) / 2.0f * f, (aabb_min.z + aabb_max.z) / 2.0f * f, 0.0f}; + vec4_t aabb_min; + vec4_t aabb_max; + mesh_data_calculate_aabb_min_max(raw, &aabb_min, &aabb_max); + return (vec4_t){(aabb_min.x + aabb_max.x) / 2.0f, (aabb_min.y + aabb_max.y) / 2.0f, (aabb_min.z + aabb_max.z) / 2.0f, 0.0f}; } void tab_meshes_make_preview(mesh_object_t *o) { @@ -700,7 +765,7 @@ void tab_meshes_make_preview(mesh_object_t *o) { vec4_t aabb = mesh_data_calculate_aabb(o->data); f32 r = math_max(aabb.x, math_max(aabb.y, aabb.z)); f32 s = 0.5 / r; - if (o->base->parent == NULL || o->base->parent == _scene_scene_parent) { + if (o->base->parent == NULL || o->base->parent == _scene_root) { s *= o->base->transform->scale.x; } s *= o->data->scale_pos; @@ -775,6 +840,18 @@ void tab_meshes_apply_visible(mesh_object_t *o) { util_mesh_visibility_changed(); } +static char *tab_meshes_mesh_info(mesh_object_t *o, i32 i) { + i32 owner = util_mesh_data_owner(o->data); + if (!util_mesh_data_is_shared(o->data)) { + return o->base->name; + } + if (owner == i) { + // Mesh data linked by other objects + return o->base->name; + } + return string("%s@%s", o->base->name, g_project->_->paint_objects->buffer[owner]->base->name); +} + void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { i32 step = g_theme->ELEMENT_H; f32 center = (step / 2.0) * UI_SCALE(); @@ -782,6 +859,14 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { f32 uix = g_ui->_x; f32 uiy = g_ui->_y; + if (base_is_dragging && base_drag_material != NULL && context_in_meshes()) { + f32 absy = g_ui->_window_y + g_ui->_y; + if (mouse_y > absy && mouse_y < absy + step * 2) { + tab_meshes_material_drop_index = i; + ui_rect(1, 0, g_ui->_w / (float)UI_SCALE() - 2, step * 2, g_theme->HIGHLIGHT_COL, 2); + } + } + // Highlight drag destination if (base_is_dragging && base_drag_mesh != NULL && context_in_meshes()) { f32 absy = g_ui->_window_y + g_ui->_y; @@ -826,7 +911,7 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { ui_image(preview, 0xffffffff, icon_h); if (g_ui->is_hovered) { ui_tooltip_image(preview, 0); - ui_tooltip(o->base->name); + ui_tooltip(tab_meshes_mesh_info(o, i)); } } else { @@ -836,7 +921,7 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { } // Material override - i32 override_idx = tab_meshes_get_override(o); + i32 override_idx = tab_meshes_get_linked_override(o); f32 name_right = uix + uiw; if (override_idx >= 0 && override_idx < g_project->_->materials->length) { slot_material_t *slot = g_project->_->materials->buffer[override_idx]; @@ -867,7 +952,10 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { char *new_name = string_copy(ui_text_input(tab_meshes_mesh_name_handle, "", UI_ALIGN_LEFT, true, false)); tab_stages_rename_object(o->base->name, new_name); o->base->name = new_name; - o->data->name = string_copy(o->base->name); + // Mesh data shared by linked duplicates is named after the object holding it + if (util_mesh_data_owner(o->data) == i) { + o->data->name = string_copy(o->base->name); + } if (g_ui->text_selected_handle != tab_meshes_mesh_name_handle) { tab_meshes_mesh_name_edit = -1; } @@ -880,7 +968,7 @@ void tab_meshes_draw_mesh_slot(mesh_object_t *o, i32 i) { bool hovered = g_ui->enabled && g_ui->input_enabled && g_ui->input_x > g_ui->_window_x + row_left && g_ui->input_x < g_ui->_window_x + uix + uiw && g_ui->input_y > g_ui->_window_y + uiy && g_ui->input_y < g_ui->_window_y + uiy + step * 2 * UI_SCALE(); if (hovered) { - ui_tooltip(o->base->name); + ui_tooltip(tab_meshes_mesh_info(o, i)); if (g_ui->input_started) { g_context->paint_object = o; ui_header_handle->redraws = 2; @@ -946,9 +1034,34 @@ void tab_meshes_highlight_odd_lines() { } } +static void tab_meshes_scroll_to_slot(i32 index) { + i32 row = 0; + for (i32 i = 0; i < index; ++i) { + if (!tab_meshes_slot_hidden(g_project->_->paint_objects->buffer[i])) { + ++row; + } + } + f32 slot_h = g_theme->ELEMENT_H * 2 * UI_SCALE(); + f32 top = g_ui->window_header_h + g_ui->current_window->scroll_offset + 2 + row * slot_h; + if (top < g_ui->window_header_h) { + g_ui->current_window->scroll_offset += g_ui->window_header_h - top; + } + else if (top + slot_h > g_ui->_window_h) { + g_ui->current_window->scroll_offset -= top + slot_h - g_ui->_window_h; + } +} + void tab_meshes_draw(ui_handle_t *htab) { if (ui_tab(htab, tr("Meshes"), false, -1, false) && g_ui->_window_h > ui_statusbar_default_h * UI_SCALE()) { + bool in_window = ui_input_in_rect(g_ui->_window_x, g_ui->_window_y, g_ui->_window_w, g_ui->_window_h); + if (in_window && g_ui->is_ctrl_down && g_ui->is_key_pressed && g_ui->key_code == KEY_CODE_F) { + tab_meshes_search_show = true; + tab_meshes_search_focus = true; + g_ui->is_key_pressed = false; + g_ui->key_code = 0; + } + ui_begin_sticky(); f32_array_t *row = f32_array_create_from_raw( (f32[]){ @@ -972,15 +1085,55 @@ void tab_meshes_draw(ui_handle_t *htab) { ui_menu_draw(&tab_meshes_draw_edit, -1, -1); } + tab_meshes_search_handle = ui_handle(__ID__); + if (tab_meshes_search_show) { + bool search_selected = g_ui->text_selected_handle == tab_meshes_search_handle; + tab_meshes_search_handle->text = string_copy(ui_text_input(tab_meshes_search_handle, tr("Search"), UI_ALIGN_LEFT, true, true)); + if (g_ui->is_hovered) { + ui_tooltip(tr("esc to cancel")); + } + if (tab_meshes_search_focus) { // Ctrl+f to open + tab_meshes_search_focus = false; + ui_start_text_edit(tab_meshes_search_handle, UI_ALIGN_LEFT); + g_ui->cursor_x = string_length(tab_meshes_search_handle->text); + g_ui->highlight_anchor = 0; + } + if ((search_selected || in_window) && g_ui->is_escape_down) { + tab_meshes_search_show = false; + tab_meshes_search_handle->text = ""; + } + } + ui_end_sticky(); g_ui->_y += 2; tab_meshes_highlight_odd_lines(); + tab_meshes_material_drop_index = -1; + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { mesh_object_t *o = g_project->_->paint_objects->buffer[i]; + if (tab_meshes_slot_hidden(o)) { + continue; + } tab_meshes_draw_mesh_slot(o, i); } + + if (in_window && !g_ui->is_typing && g_ui->is_key_pressed && (g_ui->key_code == KEY_CODE_UP || g_ui->key_code == KEY_CODE_DOWN)) { + i32 step = g_ui->key_code == KEY_CODE_UP ? -1 : 1; + i32 i = array_index_of(g_project->_->paint_objects, g_context->paint_object); + while ((i += step) >= 0 && i < g_project->_->paint_objects->length) { + mesh_object_t *candidate = g_project->_->paint_objects->buffer[i]; + if (tab_meshes_slot_hidden(candidate)) { + continue; + } + g_context->paint_object = candidate; + ui_header_handle->redraws = 2; + ui_base_hwnds->buffer[TAB_AREA_SIDEBAR0]->redraws = 2; + tab_meshes_scroll_to_slot(i); + break; + } + } } } diff --git a/paint/sources/util/util_mesh.c b/paint/sources/util/util_mesh.c index 4354f0b9..eff0560d 100644 --- a/paint/sources/util/util_mesh.c +++ b/paint/sources/util/util_mesh.c @@ -48,6 +48,88 @@ mesh_object_t_array_t *util_mesh_get_visible() { return ar; } +mesh_object_t_array_t *util_mesh_dedup_data(mesh_object_t_array_t *objects) { + mesh_object_t_array_t *ar = any_array_create_from_raw((void *[]){}, 0); + for (i32 i = 0; i < objects->length; ++i) { + mesh_object_t *p = objects->buffer[i]; + bool seen = false; + for (i32 j = 0; j < ar->length && !seen; ++j) { + seen = ar->buffer[j]->data == p->data; + } + if (!seen) { + any_array_push(ar, p); + } + } + return ar; +} + +mesh_object_t_array_t *util_mesh_get_unique_data() { + return util_mesh_dedup_data(g_project->_->paint_objects); +} + +i32 util_mesh_data_owner(mesh_data_t *data) { + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + if (g_project->_->paint_objects->buffer[i]->data == data) { + return i; + } + } + return -1; +} + +bool util_mesh_data_is_shared(mesh_data_t *data) { + i32 users = 0; + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + if (g_project->_->paint_objects->buffer[i]->data == data) { + users++; + } + } + return users > 1; +} + +void util_mesh_sync_scale_world(mesh_data_t *data) { + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + mesh_object_t *p = g_project->_->paint_objects->buffer[i]; + if (p->data == data && p->base->transform->scale_world != data->scale_pos) { + p->base->transform->scale_world = data->scale_pos; + transform_build_matrix(p->base->transform); + } + } +} + +void util_mesh_unshare_data(mesh_object_t *o) { + if (!util_mesh_data_is_shared(o->data)) { + return; + } + mesh_object_set_data(o, util_mesh_data_duplicate(o->data)); + o->data->name = string_copy(o->base->name); +} + +char *util_mesh_link_name(i32 source_index, char *object_name) { + return string("%s@%d", object_name, source_index); +} + +bool util_mesh_link_parse(char *name, i32 *source_index, char **object_name) { + if (name == NULL) { + return false; + } + i32 sep = string_last_index_of(name, "@"); + if (sep < 0) { + return false; + } + i32 len = string_length(name); + if (sep + 1 == len) { + return false; + } + for (i32 i = sep + 1; i < len; ++i) { + if (name[i] < '0' || name[i] > '9') { + return false; + } + } + *source_index = parse_int(substring(name, sep + 1, len)); + *object_name = substring(name, 0, sep); + return true; +} + #define ATLAS_MAX_STRIDE 8 #define ATLAS_MAX_SLOTS (ATLAS_MAX_STRIDE * ATLAS_MAX_STRIDE) @@ -255,6 +337,9 @@ void util_mesh_merge(mesh_object_t_array_t *paint_objects) { paint_objects = g_project->_->paint_objects; } } + else if (config_is_raytrace_multi()) { + paint_objects = util_mesh_dedup_data(paint_objects); + } if (paint_objects->length == 0) { return; } @@ -325,6 +410,9 @@ void util_mesh_merge_geometry() { // Keep the first object and join the geometry of the rest into it mesh_object_t *main_object = objects->buffer[0]; mat4_t inv_world = mat4_inv(main_object->base->transform->world); + for (i32 i = 0; i < objects->length; ++i) { + util_mesh_unshare_data(objects->buffer[i]); + } for (i32 i = 0; i < objects->length; ++i) { util_mesh_bake_transform(objects->buffer[i], inv_world); } @@ -374,7 +462,7 @@ void util_mesh_merge_geometry() { } void util_mesh_swap_axis(i32 a, i32 b) { - mesh_object_t_array_t *objects = g_project->_->paint_objects; + mesh_object_t_array_t *objects = util_mesh_get_unique_data(); for (i32 i = 0; i < objects->length; ++i) { mesh_object_t *o = objects->buffer[i]; // Remapping vertices, buckle up @@ -403,7 +491,7 @@ void util_mesh_swap_axis(i32 a, i32 b) { } void util_mesh_flip_normals() { - mesh_object_t_array_t *objects = g_project->_->paint_objects; + mesh_object_t_array_t *objects = util_mesh_get_unique_data(); for (i32 i = 0; i < objects->length; ++i) { mesh_object_t *o = objects->buffer[i]; vertex_array_t_array_t *vas = o->data->vertex_arrays; @@ -438,7 +526,7 @@ void util_mesh_calc_normals(bool smooth) { vec4_t vc = (vec4_t){0.0, 0.0, 0.0, 1.0}; vec4_t cb = (vec4_t){0.0, 0.0, 0.0, 1.0}; vec4_t ab = (vec4_t){0.0, 0.0, 0.0, 1.0}; - mesh_object_t_array_t *objects = g_project->_->paint_objects; + mesh_object_t_array_t *objects = util_mesh_get_unique_data(); for (i32 i = 0; i < objects->length; ++i) { mesh_object_t *o = objects->buffer[i]; mesh_data_t *g = o->data; @@ -548,11 +636,12 @@ void util_mesh_calc_normals(bool smooth) { } void util_mesh_to_origin() { - f32 dx = 0.0; - f32 dy = 0.0; - f32 dz = 0.0; - for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { - mesh_object_t *o = g_project->_->paint_objects->buffer[i]; + mesh_object_t_array_t *objects = util_mesh_get_unique_data(); + f32 dx = 0.0; + f32 dy = 0.0; + f32 dz = 0.0; + for (i32 i = 0; i < objects->length; ++i) { + mesh_object_t *o = objects->buffer[i]; i32 l = 4; f32 sc = o->data->scale_pos / 32767.0; i16_array_t *va = o->data->vertex_arrays->buffer[0]->values; @@ -586,12 +675,12 @@ void util_mesh_to_origin() { dy += (miny + maxy) / 2.0 * sc; dz += (minz + maxz) / 2.0 * sc; } - dx /= g_project->_->paint_objects->length; - dy /= g_project->_->paint_objects->length; - dz /= g_project->_->paint_objects->length; + dx /= objects->length; + dy /= objects->length; + dz /= objects->length; - for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { - mesh_object_t *o = g_project->_->paint_objects->buffer[i]; + for (i32 i = 0; i < objects->length; ++i) { + mesh_object_t *o = objects->buffer[i]; mesh_data_t *g = o->data; f32 sc = o->data->scale_pos / 32767.0; i16_array_t *va = o->data->vertex_arrays->buffer[0]->values; @@ -609,6 +698,7 @@ void util_mesh_to_origin() { } o->base->transform->scale_world = o->data->scale_pos = o->data->scale_pos = max_scale; transform_build_matrix(o->base->transform); + util_mesh_sync_scale_world(o->data); for (i32 i = 0; i < math_floor(va->length / 4.0); ++i) { va->buffer[i * 4] = math_floor((va->buffer[i * 4] * sc - dx) / (float)max_scale * 32767);