From f19f1d580eafce0e7f466bc356fb3a85bdcf1ee0 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Thu, 13 Aug 2026 15:31:09 +0200 Subject: [PATCH] paint: merge multi raytrace --- paint/sources/config.c | 20 ++-- paint/sources/context.c | 8 +- paint/sources/enums.h | 6 +- paint/sources/functions.h | 11 +- paint/sources/io/import_mesh.c | 2 +- paint/sources/render/make_mesh_preview.c | 39 ++++++- paint/sources/render/make_paint.c | 49 +++++---- paint/sources/render/make_texcoord.c | 23 ++-- paint/sources/render/render_gizmo.c | 5 + paint/sources/render/render_path_raytrace.c | 50 ++++----- paint/sources/types.h | 1 - paint/sources/ui/box_preferences.c | 9 +- paint/sources/ui/tab_meshes.c | 9 +- paint/sources/uniforms.c | 41 ++++---- paint/sources/util/util_layer.c | 31 +++++- paint/sources/util/util_mesh.c | 111 +++++++++++++------- 16 files changed, 272 insertions(+), 143 deletions(-) diff --git a/paint/sources/config.c b/paint/sources/config.c index c5f83454..924e6ea5 100644 --- a/paint/sources/config.c +++ b/paint/sources/config.c @@ -126,7 +126,6 @@ void config_save() { json_encode_f32("brush_alpha_discard", g_config->brush_alpha_discard); json_encode_i32("dilate_radius", g_config->dilate_radius); json_encode_string("blender", g_config->blender); - json_encode_i32("scene_atlas_res", g_config->scene_atlas_res); json_encode_bool("grid_snap", g_config->grid_snap); json_encode_bool("experimental", g_config->experimental); json_encode_i32("neural_res", g_config->neural_res); @@ -255,7 +254,6 @@ void config_init() { g_config->show_asset_names = false; g_config->dilate_radius = 2; g_config->blender = ""; - g_config->scene_atlas_res = TEXTURE_RES_RES8192; g_config->pathtrace_mode = PATHTRACE_MODE_FAST; g_config->grid_snap = false; g_config->view2d_grid_show = false; @@ -436,11 +434,6 @@ i32 config_get_layer_res() { return config_texture_res_size(res); } -i32 config_get_scene_atlas_res() { - i32 res = g_config->scene_atlas_res; - return config_texture_res_size(res); -} - void config_set_texture_res(i32 pos) { if (pos != TEXTURE_RES_CUSTOM) { f32 res = (f32)config_texture_res_size(pos); @@ -517,3 +510,16 @@ bool config_is_iphone() { return sys_display_ppi() > 330; } #endif + +bool config_is_raytrace_fast() { + return g_config->pathtrace_mode == PATHTRACE_MODE_FAST || g_config->pathtrace_mode == PATHTRACE_MODE_MULTI_FAST; +} + +bool config_is_raytrace_multi() { + bool multi = g_config->pathtrace_mode == PATHTRACE_MODE_MULTI_FAST || g_config->pathtrace_mode == PATHTRACE_MODE_MULTI_QUALITY; + return multi && g_context->viewport_mode == VIEWPORT_MODE_PATH_TRACE; +} + +void config_apply_raytrace_multi() { + gpu_raytrace_multi = config_is_raytrace_multi(); +} diff --git a/paint/sources/context.c b/paint/sources/context.c index fa4e3859..8721fa6a 100644 --- a/paint/sources/context.c +++ b/paint/sources/context.c @@ -325,10 +325,6 @@ void context_select_paint_object(mesh_object_t *o) { p->skip_context = "paint"; } - // #ifdef is_forge - // g_context->paint_object->skip_context = ""; - // #endif - g_context->paint_object = o; i32 mask = slot_layer_get_object_mask(g_context->layer); @@ -467,6 +463,10 @@ void context_set_viewport_mode(viewport_mode_t mode) { } make_material_parse_mesh_material(); + if (g_context->viewport_mode == VIEWPORT_MODE_PATH_TRACE) { + util_mesh_merge(NULL); + } + // Rotate mode is not supported for path tracing yet if (g_context->viewport_mode == VIEWPORT_MODE_PATH_TRACE && g_context->camera_controls == CAMERA_CONTROLS_ROTATE) { g_context->camera_controls = CAMERA_CONTROLS_ORBIT; diff --git a/paint/sources/enums.h b/paint/sources/enums.h index 8767fe8f..3dc1a1f0 100644 --- a/paint/sources/enums.h +++ b/paint/sources/enums.h @@ -108,8 +108,10 @@ typedef enum { } export_destination_t; typedef enum { - PATHTRACE_MODE_FAST = 0, - PATHTRACE_MODE_QUALITY = 1, + PATHTRACE_MODE_FAST = 0, + PATHTRACE_MODE_QUALITY = 1, + PATHTRACE_MODE_MULTI_FAST = 2, + PATHTRACE_MODE_MULTI_QUALITY = 3, } pathtrace_mode_t; typedef enum { diff --git a/paint/sources/functions.h b/paint/sources/functions.h index efc9543d..5e886595 100644 --- a/paint/sources/functions.h +++ b/paint/sources/functions.h @@ -81,6 +81,7 @@ void layers_merge_down(); slot_layer_t *layers_merge_group(slot_layer_t *l); void layers_merge_layer(slot_layer_t *l0, slot_layer_t *l1, bool use_mask); slot_layer_t *layers_flatten(bool height_to_normal, slot_layer_t_array_t *layers); +void layers_draw_mesh_materials(); void layers_on_resized(); void layers_create_filter(); void layers_create_image_layer(asset_t *asset); @@ -110,7 +111,6 @@ void config_apply(); i32 config_get_super_sample_quality(f32 f); f32 config_get_super_sample_size(i32 i); i32 config_get_layer_res(); -i32 config_get_scene_atlas_res(); void config_set_texture_res(i32 pos); i32 config_get_texture_res_x(); i32 config_get_texture_res_y(); @@ -119,6 +119,9 @@ void config_load_theme(char *theme, bool tag_redraw); void config_enable_plugin(char *f); void config_disable_plugin(char *f); bool config_is_iphone(); +bool config_is_raytrace_fast(); +bool config_is_raytrace_multi(); +void config_apply_raytrace_multi(); void box_export_show_textures(); void box_export_show_bake_material(); void box_export_show_mesh(); @@ -228,7 +231,7 @@ void util_layer_repaint_path(slot_layer_t *l); void util_layer_remove_path_point(slot_layer_t *l); void util_layer_clear_path_points(slot_layer_t *l); f32 util_layer_brush_screen_radius(); -void make_texcoord_run(node_shader_t *kong); +void make_texcoord_run(node_shader_t *kong, bool is_atlas); void context_init(); void context_select_material(i32 i); void context_set_material(slot_material_t *m); @@ -343,6 +346,7 @@ void render_path_preview_init(); void render_path_preview_commands_preview(); void render_path_preview_commands_decal(); node_shader_context_t *make_paint_run(material_t *data, material_context_t *matcon); +node_shader_context_t *make_paint_run_context(material_t *data, material_context_t *matcon, char *context_id); node_shader_context_t *make_mesh_preview_run(material_t *data, material_context_t *matcon, bool viewport); material_data_t *make_mesh_preview_viewport(slot_material_t *slot); void import_sound_run(char *path); @@ -607,7 +611,8 @@ void util_mesh_decimate(f32 strength); void util_mesh_smooth(); void util_mesh_bevel(f32 amount); void util_mesh_subdivide(); -void util_mesh_pack_uvs(i16_array_t *texa); +i32 util_mesh_atlas_stride(); +i32 util_mesh_atlas_slot(object_t *object); void ui_header_init(); void ui_header_render_ui(); void ui_header_draw_tool_properties(); diff --git a/paint/sources/io/import_mesh.c b/paint/sources/io/import_mesh.c index 28d4f8f7..e60575dc 100644 --- a/paint/sources/io/import_mesh.c +++ b/paint/sources/io/import_mesh.c @@ -235,7 +235,7 @@ void import_mesh_add_mesh(raw_mesh_t *mesh) { } mesh_data_t *raw = import_mesh_raw_mesh(mesh); - // util_mesh_pack_uvs(mesh->texa); + mesh_data_t *md = mesh_data_create(raw); md->_->skin_blob = mesh->blob; gc_unroot(mesh->blob); diff --git a/paint/sources/render/make_mesh_preview.c b/paint/sources/render/make_mesh_preview.c index 6632a635..14eafeb0 100644 --- a/paint/sources/render/make_mesh_preview.c +++ b/paint/sources/render/make_mesh_preview.c @@ -149,13 +149,48 @@ material_data_t *make_mesh_preview_viewport(slot_material_t *slot) { shader_context_load(con->data); material_context_load(mcon); + tool_type_t _tool = g_context->tool; + i32 _fill_type = g_context->fill_type; + slot_layer_t *_layer = g_context->layer; + bool _select_active = g_context->select_active; + bool _colorid_picked = g_context->colorid_picked; + bool _picker_paint_mask = g_context->picker_paint_mask; + gpu_texture_t *_brush_stencil_image = g_context->brush_stencil_image; + gpu_texture_t *_brush_mask_image = g_context->brush_mask_image; + blend_type_t _brush_blending = g_context->brush_blending; + g_context->tool = TOOL_TYPE_FILL; + g_context->fill_type = FILL_TYPE_OBJECT; + g_context->layer = GC_ALLOC_INIT(slot_layer_t, {.fill_material = slot, .uv_type = UV_TYPE_UVMAP, .scale = 1.0, .visible = true}); + g_context->select_active = false; + g_context->colorid_picked = false; + g_context->picker_paint_mask = false; + g_context->brush_stencil_image = NULL; + g_context->brush_mask_image = NULL; + g_context->brush_blending = BLEND_TYPE_MIX; + + material_context_t *amcon = GC_ALLOC_INIT(material_context_t, {.name = "atlas", .bind_textures = any_array_create_from_raw((void *[]){}, 0)}); + material_t *amm = GC_ALLOC_INIT(material_t, {.name = "Material", .canvas = slot->canvas}); + node_shader_context_t *acon = make_paint_run_context(amm, amcon, "atlas"); + shader_context_load(acon->data); + material_context_load(amcon); + + g_context->tool = _tool; + g_context->fill_type = _fill_type; + g_context->layer = _layer; + g_context->select_active = _select_active; + g_context->colorid_picked = _colorid_picked; + g_context->picker_paint_mask = _picker_paint_mask; + g_context->brush_stencil_image = _brush_stencil_image; + g_context->brush_mask_image = _brush_mask_image; + g_context->brush_blending = _brush_blending; + shader_data_t *sd = GC_ALLOC_INIT(shader_data_t, {0}); sd->name = string("_material_%s", i32_to_string(slot->id)); - sd->contexts = any_array_create_from_raw((void *[]){con->data}, 1); + sd->contexts = any_array_create_from_raw((void *[]){con->data, acon->data}, 2); material_data_t *md = GC_ALLOC_INIT(material_data_t, {0}); md->name = sd->name; - md->contexts = any_array_create_from_raw((void *[]){mcon}, 1); + md->contexts = any_array_create_from_raw((void *[]){mcon, amcon}, 2); md->_ = GC_ALLOC_INIT(material_data_runtime_t, {0}); md->_->shader = sd; md->_->uid = 0; diff --git a/paint/sources/render/make_paint.c b/paint/sources/render/make_paint.c index c1c0edf2..3a66b8bb 100644 --- a/paint/sources/render/make_paint.c +++ b/paint/sources/render/make_paint.c @@ -98,33 +98,39 @@ string_array_t *make_paint_color_attachments() { } node_shader_context_t *make_paint_run(material_t *data, material_context_t *matcon) { + return make_paint_run_context(data, matcon, "paint"); +} + +node_shader_context_t *make_paint_run_context(material_t *data, material_context_t *matcon, char *context_id) { bool picking_tool = g_context->tool == TOOL_TYPE_COLORID || g_context->tool == TOOL_TYPE_PICKER || g_context->tool == TOOL_TYPE_MATERIAL || g_context->tool == TOOL_TYPE_CURSOR; + bool is_atlas = string_equals(context_id, "atlas"); if (g_context->layer->texpaint_sculpt != NULL && !picking_tool) { return sculpt_make_sculpt_run(data, matcon); } - char *context_id = "paint"; - shader_context_t *props = GC_ALLOC_INIT(shader_context_t, {.name = context_id, - .depth_write = false, - .compare_mode = "always", - .cull_mode = "none", - .vertex_elements = any_array_create_from_raw( + shader_context_t *props = GC_ALLOC_INIT(shader_context_t, {.name = context_id, + .depth_write = false, + .compare_mode = "always", + .cull_mode = "none", + .vertex_elements = any_array_create_from_raw( (void *[]){ GC_ALLOC_INIT(vertex_element_t, {.name = "pos", .data = "short4norm"}), GC_ALLOC_INIT(vertex_element_t, {.name = "nor", .data = "short2norm"}), GC_ALLOC_INIT(vertex_element_t, {.name = "tex", .data = "short2norm"}), }, 3), - .color_attachments = make_paint_color_attachments()}); - node_shader_context_t *con_paint = node_shader_context_create(data, props); + .color_attachments = make_paint_color_attachments()}); + node_shader_context_t *con_paint = node_shader_context_create(data, props); - if (mesh_data_get_vertex_array(g_context->paint_object->data, "col") != NULL) { - node_shader_context_add_elem(con_paint, "col", "short4norm"); - } + if (!is_atlas) { + if (mesh_data_get_vertex_array(g_context->paint_object->data, "col") != NULL) { + node_shader_context_add_elem(con_paint, "col", "short4norm"); + } - if (mesh_data_get_vertex_array(g_context->paint_object->data, "tex1") != NULL) { - node_shader_context_add_elem(con_paint, "tex1", "short2norm"); + if (mesh_data_get_vertex_array(g_context->paint_object->data, "tex1") != NULL) { + node_shader_context_add_elem(con_paint, "tex1", "short2norm"); + } } con_paint->data->color_writes_red = u8_array_create_from_raw( @@ -159,7 +165,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc true, }, 4); - con_paint->allow_vcols = mesh_data_get_vertex_array(g_context->paint_object->data, "col") != NULL; + con_paint->allow_vcols = !is_atlas && mesh_data_get_vertex_array(g_context->paint_object->data, "col") != NULL; node_shader_t *kong = node_shader_context_make_kong(con_paint); @@ -189,12 +195,13 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc bool uv_island_fill = g_context->tool == TOOL_TYPE_FILL && g_context->fill_type == FILL_TYPE_UV_ISLAND; bool decal = context_is_decal(); - if (g_context->layer->uv_map == 1) { - node_shader_write_vert(kong, "var tpos: float2 = float2(input.tex1.x * 2.0 - 1.0, (1.0 - input.tex1.y) * 2.0 - 1.0);"); - } - else { - node_shader_write_vert(kong, "var tpos: float2 = float2(input.tex.x * 2.0 - 1.0, (1.0 - input.tex.y) * 2.0 - 1.0);"); + char *tuv = g_context->layer->uv_map == 1 ? "input.tex1" : "input.tex"; + if (is_atlas) { + node_shader_add_constant(kong, "atlas_transform: float3", "_atlas_transform"); + node_shader_write_vert(kong, string("var atlas_uv: float2 = %s * constants.atlas_transform.z + constants.atlas_transform.xy;", tuv)); + tuv = "atlas_uv"; } + node_shader_write_vert(kong, string("var tpos: float2 = float2(%s.x * 2.0 - 1.0, (1.0 - %s.y) * 2.0 - 1.0);", tuv, tuv)); node_shader_write_vert(kong, "output.pos = float4(tpos, 0.0, 1.0);"); @@ -304,7 +311,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc make_discard_material_id(kong, NULL); } - make_texcoord_run(kong); + make_texcoord_run(kong, is_atlas); if (g_context->tool == TOOL_TYPE_CLONE || g_context->tool == TOOL_TYPE_BLUR) { node_shader_add_texture(kong, "gbuffer2", NULL); @@ -328,7 +335,7 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc parser_material_triplanar = uv_type == UV_TYPE_TRIPLANAR && !decal; parser_material_sample_keep_aspect = decal; gc_unroot(parser_material_sample_uv_scale); - parser_material_sample_uv_scale = "constants.brush_scale"; + parser_material_sample_uv_scale = is_atlas ? "1.0" : "constants.brush_scale"; gc_root(parser_material_sample_uv_scale); shader_out_t *sout = parser_material_parse(g_context->material->canvas, con_paint, kong, matcon); parser_material_parse_emission = false; diff --git a/paint/sources/render/make_texcoord.c b/paint/sources/render/make_texcoord.c index 1e1b4443..71e6b3f3 100644 --- a/paint/sources/render/make_texcoord.c +++ b/paint/sources/render/make_texcoord.c @@ -1,7 +1,7 @@ #include "../global.h" -void make_texcoord_run(node_shader_t *kong) { +void make_texcoord_run(node_shader_t *kong, bool is_atlas) { bool fill_layer = g_context->layer->fill_material != NULL; uv_type_t uv_type = fill_layer ? g_context->layer->uv_type : g_context->brush_paint; @@ -9,8 +9,13 @@ void make_texcoord_run(node_shader_t *kong) { f32 angle = g_context->brush_angle + g_context->brush_nodes_angle; f32 uv_angle = fill_layer ? g_context->layer->angle : angle; - if (uv_type == UV_TYPE_PROJECT || decal) { // TexCoords - project + char *scale = "1.0"; + if (!is_atlas) { node_shader_add_constant(kong, "brush_scale: float", "_brush_scale"); + scale = "constants.brush_scale"; + } + + if (uv_type == UV_TYPE_PROJECT || decal) { // TexCoords - project node_shader_write_attrib_frag(kong, "var uvsp: float2 = sp.xy;"); if (fill_layer) { // Decal layer @@ -117,17 +122,16 @@ void make_texcoord_run(node_shader_t *kong) { } } - node_shader_write_attrib_frag(kong, "var tex_coord: float2 = uvsp * constants.brush_scale;"); + node_shader_write_attrib_frag(kong, string("var tex_coord: float2 = uvsp * %s;", scale)); } else if (uv_type == UV_TYPE_UVMAP) { // TexCoords - uvmap - node_shader_add_constant(kong, "brush_scale: float", "_brush_scale"); node_shader_add_out(kong, "tex_coord: float2"); if (g_context->layer->uv_map == 1) { - node_shader_write_vert(kong, "output.tex_coord = input.tex1 * constants.brush_scale;"); + node_shader_write_vert(kong, string("output.tex_coord = input.tex1 * %s;", scale)); } else { - node_shader_write_vert(kong, "output.tex_coord = input.tex * constants.brush_scale;"); + node_shader_write_vert(kong, string("output.tex_coord = input.tex * %s;", scale)); } if (uv_angle > 0.0) { @@ -141,14 +145,13 @@ void make_texcoord_run(node_shader_t *kong) { else { // UV_TYPE_TRIPLANAR, TexCoords - triplanar kong->frag_wposition = true; kong->frag_n = true; - node_shader_add_constant(kong, "brush_scale: float", "_brush_scale"); node_shader_write_attrib_frag(kong, "var tri_weight: float3 = input.wnormal * input.wnormal;"); node_shader_write_attrib_frag(kong, "var tri_max: float = max(tri_weight.x, max(tri_weight.y, tri_weight.z));"); node_shader_write_attrib_frag(kong, "tri_weight = max3(tri_weight - float3(tri_max * 0.75, tri_max * 0.75, tri_max * 0.75), float3(0.0, 0.0, 0.0));"); node_shader_write_attrib_frag(kong, "var tex_coord_blend: float3 = tri_weight * (1.0 / (tri_weight.x + tri_weight.y + tri_weight.z));"); - node_shader_write_attrib_frag(kong, "var tex_coord: float2 = input.wposition.yz * constants.brush_scale * 0.5;"); - node_shader_write_attrib_frag(kong, "var tex_coord1: float2 = input.wposition.xz * constants.brush_scale * 0.5;"); - node_shader_write_attrib_frag(kong, "var tex_coord2: float2 = input.wposition.xy * constants.brush_scale * 0.5;"); + node_shader_write_attrib_frag(kong, string("var tex_coord: float2 = input.wposition.yz * %s * 0.5;", scale)); + node_shader_write_attrib_frag(kong, string("var tex_coord1: float2 = input.wposition.xz * %s * 0.5;", scale)); + node_shader_write_attrib_frag(kong, string("var tex_coord2: float2 = input.wposition.xy * %s * 0.5;", scale)); if (uv_angle != 0.0) { node_shader_add_constant(kong, "brush_angle: float2", "_brush_angle"); node_shader_write_attrib_frag(kong, "tex_coord = float2(tex_coord.x * constants.brush_angle.x - tex_coord.y * constants.brush_angle.y, tex_coord.x " diff --git a/paint/sources/render/render_gizmo.c b/paint/sources/render/render_gizmo.c index aec55e95..81bb2839 100644 --- a/paint/sources/render/render_gizmo.c +++ b/paint/sources/render/render_gizmo.c @@ -90,6 +90,11 @@ void render_gizmo_update() { transform_build_matrix(paint_object->transform); ui_header_handle->redraws = 2; + if (config_is_raytrace_multi()) { + render_path_raytrace_ready = false; + g_context->ddirty = 2; + } + physics_body_t *pb = paint_object->_->body; if (pb != NULL) { physics_body_sync_transform(pb); diff --git a/paint/sources/render/render_path_raytrace.c b/paint/sources/render/render_path_raytrace.c index b5a78791..5efa7599 100644 --- a/paint/sources/render/render_path_raytrace.c +++ b/paint/sources/render/render_path_raytrace.c @@ -16,10 +16,10 @@ void render_path_raytrace_commands(bool use_live_layer) { render_path_raytrace_init_shader = true; } char *ext = ""; - // if (g_context->tool == TOOL_TYPE_CURSOR) { - // ext = "forge_"; - // } - char *mode = g_config->pathtrace_mode == PATHTRACE_MODE_FAST ? "core" : "full"; + if (config_is_raytrace_multi()) { + ext = "multi_"; + } + char *mode = config_is_raytrace_fast() ? "core" : "full"; render_path_raytrace_raytrace_init(string("raytrace_brute_%s%s%s", ext, mode, render_path_raytrace_ext), true); gc_unroot(render_path_raytrace_last_envmap); render_path_raytrace_last_envmap = NULL; @@ -137,12 +137,12 @@ void render_path_raytrace_build_data() { mesh_object_t *mo = !context_layer_filter_used() ? g_context->merged_object : g_context->paint_object; - // if (g_context->tool == TOOL_TYPE_CURSOR) { - // render_path_raytrace_transform = mo->base->transform->world_unpack; - // } - // else { - render_path_raytrace_transform = mat4_identity(); - // } + if (config_is_raytrace_multi()) { + render_path_raytrace_transform = mo->base->transform->world_unpack; + } + else { + render_path_raytrace_transform = mat4_identity(); + } f32 sc = mo->base->transform->scale.x * mo->data->scale_pos; if (mo->base->parent != NULL) { @@ -174,20 +174,21 @@ void render_path_raytrace_raytrace_init(char *shader_name, bool build) { } { + config_apply_raytrace_multi(); _gpu_raytrace_as_init(); - // if (g_context->tool == TOOL_TYPE_CURSOR) { - // for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { - // mesh_object_t *po = g_project->_->paint_objects->buffer[i]; - // if (!po->base->visible) { - // continue; - // } - // _gpu_raytrace_as_add(po->data->_->vertex_buffer, po->data->_->index_buffer, po->base->transform->world_unpack); - // } - // } - // else { - _gpu_raytrace_as_add(render_path_raytrace_vb, render_path_raytrace_ib, render_path_raytrace_transform); - // } + if (config_is_raytrace_multi()) { + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + mesh_object_t *po = g_project->_->paint_objects->buffer[i]; + if (!po->base->visible) { + continue; + } + _gpu_raytrace_as_add(po->data->_->vertex_buffer, po->data->_->index_buffer, po->base->transform->world_unpack); + } + } + else { + _gpu_raytrace_as_add(render_path_raytrace_vb, render_path_raytrace_ib, render_path_raytrace_transform); + } gpu_buffer_t *vb_full = g_context->merged_object->data->_->vertex_buffer; gpu_buffer_t *ib_full = g_context->merged_object->data->_->index_buffer; @@ -197,8 +198,9 @@ void render_path_raytrace_raytrace_init(char *shader_name, bool build) { } void render_path_raytrace_draw(bool use_live_layer) { - bool is_live = g_config->brush_live && render_path_paint_live_layer_drawn > 0; - if (g_context->ddirty > 1 || g_context->pdirty > 0 || is_live) { + bool is_live = g_config->brush_live && render_path_paint_live_layer_drawn > 0; + bool is_player = g_config->workspace == WORKSPACE_PLAYER; + if (g_context->ddirty > 1 || g_context->pdirty > 0 || is_live || is_player) { render_path_raytrace_frame = 0; } diff --git a/paint/sources/types.h b/paint/sources/types.h index 23734085..05d3f1d8 100644 --- a/paint/sources/types.h +++ b/paint/sources/types.h @@ -139,7 +139,6 @@ typedef struct config { f32 brush_alpha_discard; i32 dilate_radius; char *blender; - i32 scene_atlas_res; bool grid_snap; bool experimental; i32 neural_res; diff --git a/paint/sources/ui/box_preferences.c b/paint/sources/ui/box_preferences.c index 9d3cdc6f..2eedb7f7 100644 --- a/paint/sources/ui/box_preferences.c +++ b/paint/sources/ui/box_preferences.c @@ -413,10 +413,6 @@ void box_preferences_usage_tab() { 4); g_config->layer_res = ui_combo(h_layer_res, res_combo, tr("Default Layer Resolution"), true, UI_ALIGN_LEFT, true); - ui_handle_t *h_scene_atlas_res = ui_handle(__ID__); - h_scene_atlas_res->i = g_config->scene_atlas_res; - g_config->scene_atlas_res = ui_combo(h_scene_atlas_res, res_combo, tr("Scene Atlas Resolution"), true, UI_ALIGN_LEFT, true); - ui_handle_t *h_server = ui_handle(__ID__); h_server->text = string_copy(g_config->server); g_config->server = string_copy(ui_text_input(h_server, tr("Cloud Server"), UI_ALIGN_LEFT, true, false)); @@ -615,13 +611,16 @@ void box_preferences_viewport_tab() { (void *[]){ tr("Fast"), tr("Quality"), + tr("Multi Fast"), + tr("Multi Quality"), }, - 2); + 4); g_config->pathtrace_mode = ui_combo(h_pathtrace_mode, pathtrace_mode_combo, tr("Path Tracer"), true, UI_ALIGN_LEFT, true); if (h_pathtrace_mode->changed) { render_path_raytrace_ready = false; render_path_raytrace_init_shader = true; g_context->ddirty = 2; + util_mesh_merge(NULL); } ui_handle_t *h_render_mode = ui_handle(__ID__); diff --git a/paint/sources/ui/tab_meshes.c b/paint/sources/ui/tab_meshes.c index e77a131e..ea43cd75 100644 --- a/paint/sources/ui/tab_meshes.c +++ b/paint/sources/ui/tab_meshes.c @@ -85,6 +85,7 @@ void tab_meshes_on_material_deleted(i32 deleted_index) { if (changed) { g_project->mesh_materials = i32_array_create(0); g_context->ddirty = 2; + g_context->rtdirty = 2; } } @@ -117,6 +118,7 @@ void tab_meshes_on_material_reordered(i32 old_index, i32 new_index) { if (changed) { g_project->mesh_materials = i32_array_create(0); g_context->ddirty = 2; + g_context->rtdirty = 2; } } @@ -366,6 +368,7 @@ void tab_meshes_draw_context_menu() { if (hmat->changed) { tab_meshes_set_override(o, hmat->i - 1); g_context->ddirty = 2; + g_context->rtdirty = 2; g_project->mesh_materials = i32_array_create(0); } @@ -392,7 +395,7 @@ void tab_meshes_draw_context_menu() { // Physics if (g_config->experimental) { - physics_body_t *pb = o->base->_->body; + physics_body_t *pb = o->base->_->body; string_array_t *phys_combo = string_array_create(0); string_array_push(phys_combo, ""); // Empty = no physics string_array_push(phys_combo, tr("Box")); @@ -410,7 +413,7 @@ void tab_meshes_draw_context_menu() { } if (hphys->i > 0) { physics_shape_t shape = (physics_shape_t)(hphys->i - 1); - bool dynamic = shape == PHYSICS_SHAPE_BOX || shape == PHYSICS_SHAPE_SPHERE; + bool dynamic = shape == PHYSICS_SHAPE_BOX || shape == PHYSICS_SHAPE_SPHERE; sim_add_body(o->base, shape, dynamic ? 1.0 : 0.0); pb = o->base->_->body; } @@ -548,7 +551,6 @@ mesh_object_t *tab_meshes_append_shape(char *mesh_name) { raw = scene_raw->mesh_datas->buffer[0]; } - // util_mesh_pack_uvs(raw->vertex_arrays->buffer[2]->values); mesh_data_t *md = mesh_data_create(raw); md->_->handle = md->name; mesh_object_t *mo = scene_add_mesh_object(md, g_project->_->paint_objects->buffer[0]->material, NULL); @@ -568,6 +570,7 @@ mesh_object_t *tab_meshes_append_shape(char *mesh_name) { any_array_push(g_project->_->paint_objects, mo); tab_stages_add_object(mo->base->name); g_context->paint_object = mo; + util_mesh_merge(NULL); return mo; } diff --git a/paint/sources/uniforms.c b/paint/sources/uniforms.c index 96fc2a90..80be6b9e 100644 --- a/paint/sources/uniforms.c +++ b/paint/sources/uniforms.c @@ -87,12 +87,6 @@ f32 uniforms_ext_f32_link(object_t *object, material_data_t *mat, char *link) { return val; } else if (string_equals(link, "_brush_scale")) { - // if (g_context->tool == TOOL_TYPE_CURSOR) { - // i32 atlas_w = config_get_scene_atlas_res(); - // i32 item_w = config_get_layer_res(); - // i32 atlas_stride = atlas_w / (float)item_w; - // return atlas_stride; - // } bool fill = g_context->layer->fill_material != NULL; f32 val = (fill ? g_context->layer->scale : g_context->brush_scale) * g_context->brush_nodes_scale; return val; @@ -223,6 +217,14 @@ vec4_t uniforms_ext_vec3_link(object_t *object, material_data_t *mat, char *link g_context->prev_paint_vec_y = g_context->last_paint_vec_y; return v; } + else if (string_equals(link, "_atlas_transform")) { + if (!config_is_raytrace_multi()) { + return (vec4_t){0.0, 0.0, 1.0, 1.0}; + } + i32 stride = util_mesh_atlas_stride(); + i32 slot = util_mesh_atlas_slot(object); + return (vec4_t){(slot % stride) / (f32)stride, (slot / stride) / (f32)stride, 1.0 / stride, 1.0}; + } else if (string_equals(link, "_decal_layer_loc")) { v = (vec4_t){g_context->layer->decal_mat.m30, g_context->layer->decal_mat.m31, g_context->layer->decal_mat.m32, 1.0}; return v; @@ -364,26 +366,29 @@ void uniforms_ext_cache_uv_map(void *_) { util_uv_cache_uv_map(); } +static gpu_texture_t *_uniforms_ext_undo_target(char *name) { + render_target_t *rt = any_map_get(render_path_render_targets, name); + if (rt == NULL) { + rt = any_map_get(render_path_render_targets, "empty_black"); + } + return rt->_image; +} + gpu_texture_t *uniforms_ext_tex_link(object_t *object, material_data_t *mat, char *link) { if (string_equals(link, "_texpaint_undo")) { - i32 i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1; - render_target_t *rt = any_map_get(render_path_render_targets, string("texpaint_undo%d", i)); - return rt->_image; + i32 i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1; + return _uniforms_ext_undo_target(string("texpaint_undo%d", i)); } else if (string_equals(link, "_texpaint_nor_undo")) { - i32 i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1; - render_target_t *rt = any_map_get(render_path_render_targets, string("texpaint_nor_undo%d", i)); - return rt->_image; + i32 i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1; + return _uniforms_ext_undo_target(string("texpaint_nor_undo%d", i)); } else if (string_equals(link, "_texpaint_pack_undo")) { - i32 i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1; - render_target_t *rt = any_map_get(render_path_render_targets, string("texpaint_pack_undo%d", i)); - return rt->_image; + i32 i = history_undo_i - 1 < 0 ? g_config->undo_steps - 1 : history_undo_i - 1; + return _uniforms_ext_undo_target(string("texpaint_pack_undo%d", i)); } else if (string_equals(link, "_texpaint_sculpt_undo")) { - // Per-frame accumulation reference - render_target_t *rt = any_map_get(render_path_render_targets, "texpaint_sculpt_ref"); - return rt->_image; + return _uniforms_ext_undo_target("texpaint_sculpt_ref"); // Per-frame accumulation reference } else if (string_equals(link, "_texcolorid")) { if (g_project->_->assets->length == 0) { diff --git a/paint/sources/util/util_layer.c b/paint/sources/util/util_layer.c index 02ca7e2c..897a49c7 100644 --- a/paint/sources/util/util_layer.c +++ b/paint/sources/util/util_layer.c @@ -703,10 +703,6 @@ void layers_create_fill_layer_on_next_frame(void *_) { } void layers_create_fill_layer(uv_type_t uv_type, mat4_t decal_mat, i32 position) { - // if (g_context->tool == TOOL_TYPE_CURSOR) { - // return; - // } - _layers_uv_type = uv_type; _layers_decal_mat = decal_mat; _layers_position = position; @@ -1049,6 +1045,31 @@ void layers_merge_layer(slot_layer_t *l0, slot_layer_t *l1, bool use_mask) { } } +void layers_draw_mesh_materials() { + bool any = false; + for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { + if (tab_meshes_get_override(g_project->_->paint_objects->buffer[i]) >= 0) { + any = true; + break; + } + } + if (!any) { + return; + } + + string_array_t *additional = any_array_create_from_raw( + (void *[]){ + "expb", + "expc", + "texpaint_blend0", + }, + 3); + render_path_set_target("expa", additional, NULL, GPU_CLEAR_NONE, 0, 0.0); + render_path_bind_target("main", "gbufferD"); + render_path_bind_target("texpaint_blend1", "paintmask"); + render_path_draw_meshes("atlas"); +} + slot_layer_t *layers_flatten(bool height_to_normal, slot_layer_t_array_t *layers) { if (layers == NULL) { layers = g_project->_->layers; @@ -1171,6 +1192,8 @@ slot_layer_t *layers_flatten(bool height_to_normal, slot_layer_t_array_t *layers } } + layers_draw_mesh_materials(); + slot_layer_t *l0 = GC_ALLOC_INIT(slot_layer_t, {.texpaint = layers_expa, .texpaint_nor = layers_expb, .texpaint_pack = layers_expc}); // Merge height map into normal map diff --git a/paint/sources/util/util_mesh.c b/paint/sources/util/util_mesh.c index 98aaecb7..5c31d79c 100644 --- a/paint/sources/util/util_mesh.c +++ b/paint/sources/util/util_mesh.c @@ -12,21 +12,24 @@ void util_mesh_remove_merged() { } } +static bool _util_mesh_is_representative(i32 i) { + mesh_object_t_array_t *paint_objects = g_project->_->paint_objects; + if (!paint_objects->buffer[i]->base->visible) { + return false; + } + for (i32 j = 0; j < i; ++j) { + if (paint_objects->buffer[j]->base->visible && paint_objects->buffer[j]->data == paint_objects->buffer[i]->data) { + return false; + } + } + return true; +} + mesh_object_t_array_t *util_mesh_get_unique() { mesh_object_t_array_t *ar = any_array_create_from_raw((void *[]){}, 0); for (i32 i = 0; i < g_project->_->paint_objects->length; ++i) { - if (!g_project->_->paint_objects->buffer[i]->base->visible) { - continue; - } - bool found = false; - for (i32 j = 0; j < i; ++j) { - if (g_project->_->paint_objects->buffer[i]->data == g_project->_->paint_objects->buffer[j]->data) { - found = true; - break; - } - } - if (!found) { + if (_util_mesh_is_representative(i)) { any_array_push(ar, g_project->_->paint_objects->buffer[i]); } } @@ -45,6 +48,43 @@ mesh_object_t_array_t *util_mesh_get_visible() { return ar; } +#define ATLAS_MAX_STRIDE 8 +#define ATLAS_MAX_SLOTS (ATLAS_MAX_STRIDE * ATLAS_MAX_STRIDE) + +static i32 _util_mesh_atlas_stride_for(i32 count) { + i32 stride = 1; + while (stride * stride < count && stride < ATLAS_MAX_STRIDE) { + stride++; + } + return stride; +} + +static i32 util_mesh_atlas_stride_merged = 1; +static mesh_data_t *util_mesh_atlas_slot_data[ATLAS_MAX_SLOTS]; +static i32 util_mesh_atlas_slot_count = 0; +static bool util_mesh_atlas_slots_spent = false; + +i32 util_mesh_atlas_stride() { + return util_mesh_atlas_stride_merged; +} + +i32 util_mesh_atlas_slot(object_t *object) { + mesh_object_t_array_t *paint_objects = g_project->_->paint_objects; + mesh_data_t *data = NULL; + for (i32 i = 0; i < paint_objects->length; ++i) { + if (paint_objects->buffer[i]->base == object) { + data = paint_objects->buffer[i]->data; + break; + } + } + for (i32 i = 0; i < util_mesh_atlas_slot_count; ++i) { + if (util_mesh_atlas_slot_data[i] == data) { + return i; + } + } + return util_mesh_atlas_slots_spent ? ATLAS_MAX_SLOTS - 1 : 0; +} + mesh_data_t *util_mesh_data_duplicate(mesh_data_t *source) { mesh_data_t *raw = gc_alloc(sizeof(mesh_data_t)); raw->name = string_copy(source->name); @@ -86,6 +126,11 @@ static mesh_data_t *util_mesh_build_merged_data(mesh_object_t_array_t *paint_obj i32 coli = vatex1 != NULL ? 4 : 3; u32_array_t *ia = u32_array_create(ilen); + i32 atlas_stride = config_is_raytrace_multi() ? _util_mesh_atlas_stride_for(paint_objects->length) : 1; + util_mesh_atlas_stride_merged = atlas_stride; + util_mesh_atlas_slot_count = 0; + util_mesh_atlas_slots_spent = paint_objects->length > ATLAS_MAX_SLOTS; + i32 voff = 0; i32 ioff = 0; for (i32 i = 0; i < paint_objects->length; ++i) { @@ -117,8 +162,17 @@ static mesh_data_t *util_mesh_build_merged_data(mesh_object_t_array_t *paint_obj va1->buffer[j + voff * 2] = vas->buffer[1]->values->buffer[j]; } // Tex - for (i32 j = 0; j < vas->buffer[2]->values->length; ++j) { - va2->buffer[j + voff * 2] = vas->buffer[2]->values->buffer[j]; + i32 slot = i < ATLAS_MAX_SLOTS ? i : ATLAS_MAX_SLOTS - 1; + if (i < ATLAS_MAX_SLOTS) { + util_mesh_atlas_slot_data[slot] = paint_objects->buffer[i]->data; + util_mesh_atlas_slot_count = slot + 1; + } + f32 tile_step = 32767.0f / atlas_stride; + f32 tile_x = atlas_stride > 1 ? (slot % atlas_stride) * tile_step : 0.0f; + f32 tile_y = atlas_stride > 1 ? (slot / atlas_stride) * tile_step : 0.0f; + for (i32 j = 0; j < vas->buffer[2]->values->length / 2; ++j) { + va2->buffer[j * 2 + voff * 2] = vas->buffer[2]->values->buffer[j * 2] / (float)atlas_stride + tile_x; + va2->buffer[j * 2 + 1 + voff * 2] = vas->buffer[2]->values->buffer[j * 2 + 1] / (float)atlas_stride + tile_y; } // Tex1 if (vatex1 != NULL) { @@ -164,12 +218,12 @@ static mesh_data_t *util_mesh_build_merged_data(mesh_object_t_array_t *paint_obj void util_mesh_merge(mesh_object_t_array_t *paint_objects) { if (paint_objects == NULL) { - // if (g_context->tool == TOOL_TYPE_CURSOR) { - // paint_objects = util_mesh_get_unique(); - // } - // else { - paint_objects = g_project->_->paint_objects; - // } + if (config_is_raytrace_multi()) { + paint_objects = util_mesh_get_unique(); + } + else { + paint_objects = g_project->_->paint_objects; + } } if (paint_objects->length == 0) { return; @@ -699,25 +753,6 @@ void util_mesh_decimate(f32 strength) { #endif } -i32 _util_mesh_unique_data_count() { - return util_mesh_get_unique()->length; -} - -void util_mesh_pack_uvs(i16_array_t *texa) { - // Scale tex coords into global atlas - i32 atlas_w = config_get_scene_atlas_res(); - i32 item_i = _util_mesh_unique_data_count() - 1; // Add the one being imported - i32 item_w = config_get_layer_res(); - i32 atlas_stride = atlas_w / (float)item_w; - i32 atlas_step = 32767 / (float)atlas_stride; - i32 item_x = (item_i % atlas_stride) * atlas_step; - i32 item_y = math_floor(item_i / (float)atlas_stride) * atlas_step; - for (i32 i = 0; i < texa->length / 2.0; ++i) { - texa->buffer[i * 2] = texa->buffer[i * 2] / (float)atlas_stride + item_x; - texa->buffer[i * 2 + 1] = texa->buffer[i * 2 + 1] / (float)atlas_stride + item_y; - } -} - static i32 *_cc_he_vlo; static i32 *_cc_he_vhi;