From 2fd703cb18583053fa8bef163aeb92e67f2f0cf5 Mon Sep 17 00:00:00 2001 From: luboslenco Date: Thu, 28 May 2026 21:43:39 +0200 Subject: [PATCH] paint: add decal_camera_align shortcut --- paint/shaders/cursor_decal.kong | 18 ++++++++++++++---- paint/sources/context.c | 4 ++++ paint/sources/globals.h | 2 ++ paint/sources/keymap.c | 1 + paint/sources/make_texcoord.c | 18 ++++++++++++++++++ paint/sources/pipes.c | 14 ++++++++------ paint/sources/render_path_paint.c | 3 +++ paint/sources/uniforms.c | 7 +++++++ 8 files changed, 57 insertions(+), 10 deletions(-) diff --git a/paint/shaders/cursor_decal.kong b/paint/shaders/cursor_decal.kong index 99cca3b6..fff75bd3 100644 --- a/paint/shaders/cursor_decal.kong +++ b/paint/shaders/cursor_decal.kong @@ -10,6 +10,8 @@ const constants: { opacity: float; angle: float2; scale_x: float; + camera_up: float3; + camera_align: float; }; #[set(everything)] @@ -89,10 +91,18 @@ fun cursor_decal_vert(input: vert_in): vert_out { var uv2: float2 = constants.mouse - constants.tex_step * float2(4.0, 4.0); var n: float3 = normalize(get_normal(constants.mouse) + get_normal(uv1) + get_normal(uv2)); - var cam_right: float3 = constants.camera_right; - if (abs(dot(n, cam_right)) > 0.999) { cam_right = float3(0.0, 0.0, 1.0); } - var n_tan: float3 = normalize(cam_right - n * dot(cam_right, n)); - var n_bin: float3 = cross(n_tan, n); + var n_tan: float3; + var n_bin: float3; + if (constants.camera_align > 0.0) { + n_tan = constants.camera_right; + n_bin = constants.camera_up; + } + else { + var cam_right: float3 = constants.camera_right; + if (abs(dot(n, cam_right)) > 0.999) { cam_right = float3(0.0, 0.0, 1.0); } + n_tan = normalize(cam_right - n * dot(cam_right, n)); + n_bin = cross(n_tan, n); + } // Rotate tangent basis by brush angle var r_tan: float3 = constants.angle.x * n_tan + constants.angle.y * n_bin; diff --git a/paint/sources/context.c b/paint/sources/context.c index 9f72715f..a4c6061b 100644 --- a/paint/sources/context.c +++ b/paint/sources/context.c @@ -424,6 +424,10 @@ bool context_is_decal_mask() { return context_is_decal() && operator_shortcut(any_map_get(config_keymap, "decal_mask"), SHORTCUT_TYPE_DOWN); } +bool context_is_decal_camera_align() { + return context_is_decal() && operator_shortcut(any_map_get(config_keymap, "decal_camera_align"), SHORTCUT_TYPE_DOWN); +} + bool context_is_decal_mask_paint() { return context_is_decal() && operator_shortcut(string("%s+%s", any_map_get(config_keymap, "decal_mask"), any_map_get(config_keymap, "action_paint")), SHORTCUT_TYPE_DOWN); diff --git a/paint/sources/globals.h b/paint/sources/globals.h index c57be8ea..fc81cbc1 100644 --- a/paint/sources/globals.h +++ b/paint/sources/globals.h @@ -87,6 +87,8 @@ i32 pipes_cursor_decal_camera_right; i32 pipes_cursor_decal_opacity; i32 pipes_cursor_decal_angle; i32 pipes_cursor_decal_scale_x; +i32 pipes_cursor_decal_camera_up; +i32 pipes_cursor_decal_camera_align; i32 pipes_cursor_decal_gbufferd; i32 pipes_cursor_decal_texdecal; i32 pipes_cursor_decal_gbuffer0; diff --git a/paint/sources/keymap.c b/paint/sources/keymap.c index 5f03f3e5..0162f9b7 100644 --- a/paint/sources/keymap.c +++ b/paint/sources/keymap.c @@ -83,6 +83,7 @@ any_map_t *keymap_get_default() { any_map_set(keymap, "node_search", "space"); any_map_set(keymap, "operator_search", "space"); any_map_set(keymap, "decal_mask", "ctrl"); + any_map_set(keymap, "decal_camera_align", "z"); any_map_set(keymap, "select_material", "shift+number"); any_map_set(keymap, "select_layer", "alt+number"); any_map_set(keymap, "brush_opacity", "shift+f"); diff --git a/paint/sources/make_texcoord.c b/paint/sources/make_texcoord.c index b40ff569..46d6ddbc 100644 --- a/paint/sources/make_texcoord.c +++ b/paint/sources/make_texcoord.c @@ -39,7 +39,23 @@ void make_texcoord_run(node_shader_t *kong) { node_shader_add_texture(kong, "gbuffer0", NULL); node_shader_add_constant(kong, "decal_mask: float4", "_decal_mask"); node_shader_add_constant(kong, "invVP: float4x4", "_inv_view_proj_matrix"); + node_shader_add_constant(kong, "VP: float4x4", "_view_proj_matrix"); node_shader_add_constant(kong, "camera_right: float3", "_camera_right"); + node_shader_add_constant(kong, "camera_up: float3", "_camera_up"); + node_shader_add_constant(kong, "camera_align: float", "_decal_camera_align"); + + node_shader_write_attrib_frag(kong, "if (constants.camera_align > 0.0) {"); + node_shader_write_attrib_frag(kong, "var ca_depth: float = sample_lod(gbufferD, sampler_linear, constants.decal_mask.xy, 0.0).r;"); + node_shader_write_attrib_frag(kong, "var ca_coord: float2 = float2(constants.decal_mask.x * 2.0 - 1.0, 1.0 - constants.decal_mask.y * 2.0);"); + node_shader_write_attrib_frag(kong, "var ca_homog: float4 = constants.invVP * float4(ca_coord.x, ca_coord.y, ca_depth, 1.0);"); + node_shader_write_attrib_frag(kong, "var ca_clip_w: float = 1.0 / ca_homog.w;"); + node_shader_write_attrib_frag(kong, "var vp_up_y: float = (constants.VP * float4(constants.camera_up, 0.0)).y;"); + node_shader_write_attrib_frag(kong, "uvsp = sp.xy - constants.decal_mask.xy;"); + node_shader_write_attrib_frag(kong, "uvsp.x *= constants.aspect_ratio;"); + node_shader_write_attrib_frag(kong, "uvsp = uvsp * (ca_clip_w / (vp_up_y * constants.brush_radius));"); + node_shader_write_attrib_frag(kong, "}"); + + node_shader_write_attrib_frag(kong, "else {"); // When mask is active, anchor the decal at the frozen position; otherwise follow the mouse node_shader_write_attrib_frag(kong, "var decal_xy: float2 = constants.inp.xy;"); @@ -72,6 +88,8 @@ void make_texcoord_run(node_shader_t *kong) { node_shader_write_attrib_frag(kong, "uvsp = float2(dot(d_offset, d_tan), dot(d_offset, d_bin));"); node_shader_write_attrib_frag(kong, "uvsp = uvsp / decal_radius * 0.5;"); + node_shader_write_attrib_frag(kong, "}"); + if (g_context->brush_directional) { node_shader_add_constant(kong, "brush_direction: float3", "_brush_direction"); node_shader_write_attrib_frag(kong, "if (constants.brush_direction.z == 0.0) { discard; }"); diff --git a/paint/sources/pipes.c b/paint/sources/pipes.c index 8b68764e..e082f2d6 100644 --- a/paint/sources/pipes.c +++ b/paint/sources/pipes.c @@ -120,11 +120,11 @@ void pipes_init() { gc_root(pipes_invert_mask); pipes_invert_mask->vertex_shader = sys_get_shader("layer_invert.vert"); pipes_invert_mask->fragment_shader = sys_get_shader("layer_invert.frag"); - gpu_vertex_structure_t *vs = GC_ALLOC_INIT(gpu_vertex_structure_t, {0}); + gpu_vertex_structure_t *vs = GC_ALLOC_INIT(gpu_vertex_structure_t, {0}); gpu_vertex_structure_add(vs, "pos", GPU_VERTEX_DATA_F32_2X); gpu_vertex_structure_add(vs, "tex", GPU_VERTEX_DATA_F32_2X); gpu_vertex_structure_add(vs, "col", GPU_VERTEX_DATA_F32_4X); - pipes_invert_mask->input_layout = vs; + pipes_invert_mask->input_layout = vs; gpu_pipeline_compile(pipes_invert_mask); } @@ -138,10 +138,10 @@ void pipes_init() { gpu_vertex_structure_add(vs, "pos", GPU_VERTEX_DATA_F32_2X); pipes_apply_mask->input_layout = vs; gpu_pipeline_compile(pipes_apply_mask); - pipes_tex0_mask = 0; - pipes_texa_mask = 1; - pipes_offset = 0; - pipes_opac_apply_mask = pipes_get_constant_location("float"); + pipes_tex0_mask = 0; + pipes_texa_mask = 1; + pipes_offset = 0; + pipes_opac_apply_mask = pipes_get_constant_location("float"); } { @@ -242,6 +242,8 @@ void pipes_init() { pipes_cursor_decal_opacity = pipes_get_constant_location("float"); pipes_cursor_decal_angle = pipes_get_constant_location("vec2"); pipes_cursor_decal_scale_x = pipes_get_constant_location("float"); + pipes_cursor_decal_camera_up = pipes_get_constant_location("vec3"); + pipes_cursor_decal_camera_align = pipes_get_constant_location("float"); pipes_cursor_decal_gbufferd = 0; pipes_cursor_decal_texdecal = 1; pipes_cursor_decal_gbuffer0 = 2; diff --git a/paint/sources/render_path_paint.c b/paint/sources/render_path_paint.c index b9588970..7ca7cc3c 100644 --- a/paint/sources/render_path_paint.c +++ b/paint/sources/render_path_paint.c @@ -686,6 +686,9 @@ void render_path_paint_draw_cursor_decal(f32 mx, f32 my, f32 radius, f32 opacity gpu_set_float(pipes_cursor_decal_radius, radius); vec4_t right = vec4_norm(camera_object_right_world(scene_camera)); gpu_set_float3(pipes_cursor_decal_camera_right, right.x, right.y, right.z); + vec4_t up = vec4_norm(camera_object_up_world(scene_camera)); + gpu_set_float3(pipes_cursor_decal_camera_up, up.x, up.y, up.z); + gpu_set_float(pipes_cursor_decal_camera_align, context_is_decal_camera_align() ? 1.0f : 0.0f); f32 opacity = g_context->brush_opacity * g_context->brush_nodes_opacity * opacity_scale; gpu_set_float(pipes_cursor_decal_opacity, opacity); f32 angle = (g_context->brush_angle + g_context->brush_nodes_angle) * (math_pi() / 180.0); diff --git a/paint/sources/uniforms.c b/paint/sources/uniforms.c index 10d79785..bdda2dfa 100644 --- a/paint/sources/uniforms.c +++ b/paint/sources/uniforms.c @@ -104,6 +104,9 @@ f32 uniforms_ext_f32_link(object_t *object, material_data_t *mat, char *link) { vec4_t sc = mat4_get_scale(g_context->layer->decal_mat); return sc.z * 0.5; } + else if (string_equals(link, "_decal_camera_align")) { + return context_is_decal_camera_align() ? 1.0f : 0.0f; + } else if (string_equals(link, "_picker_opacity")) { return g_context->picked_color->opacity; } @@ -224,6 +227,10 @@ vec4_t uniforms_ext_vec3_link(object_t *object, material_data_t *mat, char *link v = camera_object_right_world(scene_camera); return v; } + else if (string_equals(link, "_camera_up")) { + v = camera_object_up_world(scene_camera); + return v; + } return v; }