paint: add decal_camera_align shortcut

This commit is contained in:
luboslenco
2026-05-28 21:43:39 +02:00
parent 5d93d4c86a
commit 2fd703cb18
8 changed files with 57 additions and 10 deletions
+14 -4
View File
@@ -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;
+4
View File
@@ -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);
+2
View File
@@ -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;
+1
View File
@@ -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");
+18
View File
@@ -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; }");
+8 -6
View File
@@ -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;
+3
View File
@@ -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);
+7
View File
@@ -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;
}