diff --git a/paint/sources/functions.h b/paint/sources/functions.h index a986011a..628ee849 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -561,6 +561,7 @@ void ui_box_show_message(char *title, char *text, bool c void ui_box_show_custom(void (*commands)(void), i32 mw, i32 mh, void (*on_hide)(void), bool draggable, char *title); void ui_box_hide(); void tab_meshes_draw(ui_handle_t *htab); +void tab_meshes_reset_preview_map(); void tab_scene_select_object(mesh_object_t *mo); void tab_scene_sort(); void tab_scene_import_mesh_done(); diff --git a/paint/sources/import_mesh.c b/paint/sources/import_mesh.c index 0787b5d3..9df6b143 100644 --- a/paint/sources/import_mesh.c +++ b/paint/sources/import_mesh.c @@ -102,6 +102,7 @@ void import_mesh_finish_import() { ui_view2d_hwnd->redraws = 2; render_path_raytrace_ready = false; g_context->paint_body = NULL; + tab_meshes_reset_preview_map(); } void _import_mesh_make_mesh_finish_import(void *_) { diff --git a/paint/sources/project.c b/paint/sources/project.c index 8065aaeb..51c4c4cc 100644 --- a/paint/sources/project.c +++ b/paint/sources/project.c @@ -341,6 +341,7 @@ void project_new(bool reset_layers) { if (in_use) draw_begin(current, false, 0); + tab_meshes_reset_preview_map(); base_update_workflow(); project_set_default_envmap(); context_select_tool(TOOL_TYPE_BRUSH); diff --git a/paint/sources/tab_meshes.c b/paint/sources/tab_meshes.c index 82e599cc..65270c7e 100644 --- a/paint/sources/tab_meshes.c +++ b/paint/sources/tab_meshes.c @@ -1,7 +1,10 @@ #include "global.h" -i32 _tab_meshes_draw_i; +extern buffer_t *slot_material_default_canvas; + +i32 _tab_meshes_draw_i; +any_map_t *tab_meshes_preview_map = NULL; void tab_meshes_draw_properties(mesh_object_t *o) { g_context->selected_object = o->base; @@ -358,6 +361,153 @@ 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}; +} + +void tab_meshes_make_preview(mesh_object_t *o) { + if (tab_meshes_preview_map == NULL) { + tab_meshes_preview_map = any_map_create(); + gc_root(tab_meshes_preview_map); + } + + char *uid_key = i32_to_string(o->base->uid); + gpu_texture_t *image = any_map_get(tab_meshes_preview_map, uid_key); + if (image == NULL) { + image = gpu_create_render_target(util_render_material_preview_size, util_render_material_preview_size, GPU_TEXTURE_FORMAT_RGBA64); + any_map_set(tab_meshes_preview_map, uid_key, image); + } + + g_context->material_preview = true; + + slot_material_t *mat = GC_ALLOC_INIT(slot_material_t, {0}); + mat->image = image; + mat->image_icon = gpu_create_render_target(50, 50, GPU_TEXTURE_FORMAT_RGBA64); + mat->preview_ready = true; + mat->canvas = armpack_decode(slot_material_default_canvas); + mat->canvas = util_clone_canvas(mat->canvas); // Clone to create GC references + + slot_material_t *_material = g_context->material; + g_context->material = mat; + + mesh_object_t_array_t *_scene_meshes = scene_meshes; + gc_unroot(scene_meshes); + scene_meshes = any_array_create_from_raw((void *[]){o}, 1); + gc_root(scene_meshes); + + mesh_object_t *painto = g_context->paint_object; + g_context->paint_object = o; + + g_context->saved_camera = scene_camera->base->transform->local; + mat4_t m = + (mat4_t){0.9146286343879498, 0.404295023959927, 0.000007410128652369705, 0, -0.0032648027153306235, 0.007367569133732468, 0.9999675337275382, 0, + 0.404281837254303, -0.9145989516155143, 0.008058532943908717, 0, 0.4659988049397712, -1.0687517188018691, 0.015935682577325486, 1}; + transform_set_matrix(scene_camera->base->transform, m); + f32 saved_fov = scene_camera->data->fov; + scene_camera->data->fov = 0.4; + viewport_update_camera_type(CAMERA_TYPE_PERSPECTIVE); + + world_data_t *probe = scene_world; + f32 _probe_strength = probe->strength; + probe->strength = 2; + f32 _envmap_angle = g_context->envmap_angle; + g_context->envmap_angle = 0.0; + + gpu_texture_t *_envmap = scene_world->_->envmap; + scene_world->_->envmap = g_context->preview_envmap; + + // Fit into camera + vec4_t saved_scale = o->base->transform->scale; + vec4_t saved_loc = o->base->transform->loc; + quat_t saved_rot = o->base->transform->rot; + { + vec4_t aabb = mesh_data_calculate_aabb(o->data); + f32 r = math_sqrt(aabb.x * aabb.x + aabb.y * aabb.y + aabb.z * aabb.z); + f32 s = 1.0 / r; + if (o->base->parent == NULL) { + s *= o->base->transform->scale.x; + } + s *= o->data->scale_pos; + vec4_t center = aabb_center(o->data); + o->base->transform->scale = (vec4_t){s, s, s, 1.0}; + o->base->transform->loc = (vec4_t){-s * center.x, -s * center.y, -s * center.z, 1.0}; + o->base->transform->rot = (quat_t){0, 0, 0, 1}; + transform_build_matrix(o->base->transform); + } + + _render_path_last_w = util_render_material_preview_size; + _render_path_last_h = util_render_material_preview_size; + camera_object_build_proj(scene_camera, -1.0); + camera_object_build_mat(scene_camera); + + make_material_parse_mesh_preview_material(); + void (*_commands)(void) = render_path_commands; + gc_unroot(render_path_commands); + render_path_commands = render_path_preview_commands_preview; + gc_root(render_path_commands); + render_path_render_frame(); + gc_unroot(render_path_commands); + render_path_commands = _commands; + gc_root(render_path_commands); + + g_context->material_preview = false; + _render_path_last_w = sys_w(); + _render_path_last_h = sys_h(); + + // Restore + o->base->transform->scale = saved_scale; + o->base->transform->loc = saved_loc; + o->base->transform->rot = saved_rot; + transform_build_matrix(o->base->transform); + + gc_unroot(scene_meshes); + scene_meshes = _scene_meshes; + gc_root(scene_meshes); + g_context->paint_object = painto; + + transform_set_matrix(scene_camera->base->transform, g_context->saved_camera); + viewport_update_camera_type(g_context->camera_type); + scene_camera->data->fov = saved_fov; + camera_object_build_proj(scene_camera, -1.0); + camera_object_build_mat(scene_camera); + + probe->strength = _probe_strength; + g_context->envmap_angle = _envmap_angle; + scene_world->_->envmap = _envmap; + + g_context->material = _material; + gpu_delete_texture(mat->image_icon); + + make_material_parse_mesh_material(); + g_context->ddirty = 0; +} + void tab_meshes_draw(ui_handle_t *htab) { if (ui_tab(htab, tr("Meshes"), false, -1, false) && ui->_window_h > ui_statusbar_default_h * UI_SCALE()) { @@ -382,40 +532,128 @@ void tab_meshes_draw(ui_handle_t *htab) { ui_end_sticky(); - for (i32 i = 0; i < project_paint_objects->length; ++i) { + i32 slotw = math_floor(78 * UI_SCALE()); + i32 num = math_floor(ui->_window_w / (float)slotw); + if (num == 0) { + return; + } - f32_array_t *row = f32_array_create_from_raw( - (f32[]){ - -30, - 1.0, - }, - 2); - ui_row(row); - gpu_texture_t *icons = resource_get("icons05x.k"); - rect_t *rect = resource_tile50(icons, ICON_CUBE); - i32 icon_h = 25 * UI_SCALE(); - ui_sub_image(icons, base_darker(ui->ops->theme->LABEL_COL, 0x00333333), icon_h, rect->x / 2.0, rect->y / 2.0, rect->w / 2.0, rect->h / 2.0); + f32 uix = 0.0; + f32 uiy = 0.0; - mesh_object_t *o = project_paint_objects->buffer[i]; - ui_handle_t *h = ui_handle(__ID__); - h->b = o->base->visible; - o->base->visible = ui_check(h, o->base->name, ""); - - if (ui->is_hovered && ui->input_released_r) { - _tab_meshes_draw_i = i; - ui_menu_draw(&tab_meshes_draw_context_menu, -1, -1); + for (i32 row = 0; row < math_floor(math_ceil(project_paint_objects->length / (float)num)); ++row) { + f32_array_t *ar = f32_array_create_from_raw((f32[]){}, 0); + for (i32 i = 0; i < num * 2; ++i) { + f32_array_push(ar, 1 / (float)num); } - if (h->changed) { - mesh_object_t_array_t *visibles = any_array_create_from_raw((void *[]){}, 0); - for (i32 i = 0; i < project_paint_objects->length; ++i) { - mesh_object_t *p = project_paint_objects->buffer[i]; - if (p->base->visible) { - any_array_push(visibles, p); - } + ui_row(ar); + + ui->_x += 2; + if (row > 0) { + ui->_y += UI_ELEMENT_OFFSET() * 10.0; + } + + for (i32 j = 0; j < num; ++j) { + i32 imgw = math_floor(75 * UI_SCALE()); + i32 i = j + row * num; + if (i >= project_paint_objects->length) { + ui_end_element_of_size(imgw); + ui_end_element_of_size(0); + continue; + } + + mesh_object_t *o = project_paint_objects->buffer[i]; + uix = ui->_x; + uiy = ui->_y; + + // Draw mesh preview + char *uid_key = i32_to_string(o->base->uid); + gpu_texture_t *preview = tab_meshes_preview_map != NULL ? any_map_get(tab_meshes_preview_map, uid_key) : NULL; + ui_state_t state; + if (preview != NULL) { + state = ui_image(preview, 0xffffffff, (f32)slotw); + } + else { + gpu_texture_t *icons = resource_get("icons.k"); + rect_t *rect = resource_tile50(icons, ICON_CUBE); + state = ui_sub_image(icons, ui->ops->theme->BUTTON_COL, slotw, rect->x, rect->y, rect->w, rect->h); + sys_notify_on_next_frame(tab_meshes_make_preview, o); + } + + // Selection highlight + if (g_context->paint_object == o) { + f32 _uix = ui->_x; + f32 _uiy = ui->_y; + ui->_x = uix; + ui->_y = uiy + 4; + i32 hoff = i % 2 == 1 ? 1 : 0; + i32 w = 75; + ui_fill(0, 0, w + 3, 2, ui->ops->theme->HIGHLIGHT_COL); + ui_fill(0, w - hoff + 2, w + 3, 2 + hoff, ui->ops->theme->HIGHLIGHT_COL); + ui_fill(0, 0, 2, w + 3, ui->ops->theme->HIGHLIGHT_COL); + ui_fill(w + 2, 0, 2, w + 4, ui->ops->theme->HIGHLIGHT_COL); + ui->_x = _uix; + ui->_y = _uiy; + } + + // Click to select + if (state == UI_STATE_STARTED && ui->input_y > ui->_window_y) { + g_context->paint_object = o; + } + + // Context menu + if (ui->is_hovered && ui->input_released_r) { + g_context->paint_object = o; + _tab_meshes_draw_i = i; + ui_menu_draw(&tab_meshes_draw_context_menu, -1, -1); + } + + if (ui->is_hovered) { + ui_tooltip(o->base->name); + } + + // Label + i32 check_w = UI_ELEMENT_H(); + i32 text_w = draw_string_width(ui->ops->font, ui->font_size, o->base->name); + i32 center_x = (slotw - check_w - text_w) / 2; + ui->_x = uix + (center_x > 0 ? center_x : 0); + ui->_y += slotw * 0.9 + 8; + + ui_handle_t *h = ui_handle(__ID__); + h->b = o->base->visible; + o->base->visible = ui_check(h, o->base->name, ""); + + if (h->changed) { + mesh_object_t_array_t *visibles = any_array_create_from_raw((void *[]){}, 0); + for (i32 k = 0; k < project_paint_objects->length; ++k) { + mesh_object_t *p = project_paint_objects->buffer[k]; + if (p->base->visible) { + any_array_push(visibles, p); + } + } + util_mesh_merge(visibles); + g_context->ddirty = 2; + } + + ui->_y -= slotw * 0.9 + 8; + if (i == project_paint_objects->length - 1) { + ui->_y += j == num - 1 ? imgw : imgw + UI_ELEMENT_H() + UI_ELEMENT_OFFSET(); } - util_mesh_merge(visibles); - g_context->ddirty = 2; } } } } + +void tab_meshes_reset_preview_map() { + if (tab_meshes_preview_map == NULL) { + return; + } + + any_array_t *keys = map_keys(tab_meshes_preview_map); + for (i32 i = 0; i < keys->length; ++i) { + gpu_texture_t *image = any_map_get(tab_meshes_preview_map, keys->buffer[i]); + gpu_delete_texture(image); + } + gc_unroot(tab_meshes_preview_map); + tab_meshes_preview_map = NULL; +}