paint: meshes tab previews

This commit is contained in:
luboslenco
2026-04-28 14:59:44 +02:00
parent f894433699
commit 2d8c1429b5
4 changed files with 271 additions and 30 deletions
+1
View File
@@ -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();
+1
View File
@@ -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 *_) {
+1
View File
@@ -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);
+268 -30
View File
@@ -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;
}