paint: brush camera align

This commit is contained in:
luboslenco
2026-07-11 22:58:31 +02:00
parent cbeacb9f53
commit 6052e5d6a2
14 changed files with 68 additions and 30 deletions
+10 -8
View File
@@ -8,6 +8,8 @@ const constants: {
radius: float;
camera_right: float3;
tint: float3;
camera_up: float3;
camera_align: float;
};
#[set(everything)]
@@ -40,11 +42,6 @@ fun get_normal(p0: float3, uv: float2): float3 {
return normalize(cross(p2 - p0, p1 - p0));
}
// fun create_basis(normal: float3, out tangent: float3, out binormal: float3) {
// tangent = normalize(constants.camera_right - normal * dot(constants.camera_right, normal));
// binormal = cross(tangent, normal);
// }
fun cursor_vert(input: vert_in): vert_out {
var keep: float = input.pos.x + input.nor.x; // hlsl
@@ -59,9 +56,14 @@ fun cursor_vert(input: vert_in): vert_out {
var n_tan: float3;
var n_bin: float3;
// create_basis(n, n_tan, n_bin);
n_tan = normalize(constants.camera_right - n * dot(constants.camera_right, n));
n_bin = cross(n_tan, n);
if (constants.camera_align > 0.0) {
n_tan = constants.camera_right;
n_bin = constants.camera_up;
}
else {
n_tan = normalize(constants.camera_right - n * dot(constants.camera_right, n));
n_bin = cross(n_tan, n);
}
if (vertex_id() == 0) {
wpos += normalize(-n_tan - n_bin) * 0.7 * constants.radius;
+4 -2
View File
@@ -426,8 +426,10 @@ bool context_is_decal_mask() {
return context_is_decal() && operator_shortcut(any_map_get(g_keymap, "decal_mask"), SHORTCUT_TYPE_DOWN);
}
bool context_is_decal_camera_align() {
return context_is_decal() && (g_context->decal_camera_align || operator_shortcut(any_map_get(g_keymap, "decal_camera_align"), SHORTCUT_TYPE_DOWN));
bool context_is_brush_camera_align() {
bool brush = g_context->tool == TOOL_TYPE_BRUSH || g_context->tool == TOOL_TYPE_ERASER;
return (context_is_decal() || brush) &&
(g_context->brush_camera_align || operator_shortcut(any_map_get(g_keymap, "brush_camera_align"), SHORTCUT_TYPE_DOWN));
}
bool context_is_decal_mask_paint() {
+1 -1
View File
@@ -257,7 +257,7 @@ bool context_in_browser();
bool context_is_decal();
bool context_is_decal_mask();
bool context_is_decal_mask_paint();
bool context_is_decal_camera_align();
bool context_is_brush_camera_align();
bool context_is_floating_toolbar();
void context_set_viewport_mode(viewport_mode_t mode);
void context_load_envmap();
+2
View File
@@ -83,6 +83,8 @@ i32 pipes_cursor_tex_step;
i32 pipes_cursor_radius;
i32 pipes_cursor_camera_right;
i32 pipes_cursor_tint;
i32 pipes_cursor_camera_up;
i32 pipes_cursor_camera_align;
i32 pipes_cursor_gbufferd;
gpu_pipeline_t *pipes_cursor_decal;
i32 pipes_cursor_decal_vp;
+1 -1
View File
@@ -83,7 +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, "brush_camera_align", "z");
any_map_set(keymap, "grid_snap", "x");
any_map_set(keymap, "select_material", "shift+number");
any_map_set(keymap, "select_layer", "alt+number");
@@ -251,7 +251,7 @@ static void texture_mesh_node_project(ui_node_t *node) {
// Project a camera-aligned decal
g_context->tool = TOOL_TYPE_DECAL;
g_context->decal_camera_align = true;
g_context->brush_camera_align = true;
g_context->brush_opacity = 1.0;
g_context->brush_angle = 0.0;
g_context->brush_scale = 1.0;
@@ -302,7 +302,7 @@ static void texture_mesh_node_project(ui_node_t *node) {
g_context->pdirty = 0;
g_context->tool = _tool;
g_context->decal_camera_align = false;
g_context->brush_camera_align = false;
g_context->brush_radius = _brush_radius;
g_context->brush_opacity = _brush_opacity;
g_context->brush_angle = _brush_angle;
+2
View File
@@ -214,6 +214,8 @@ void pipes_init() {
pipes_cursor_radius = pipes_get_constant_location("float");
pipes_cursor_camera_right = pipes_get_constant_location("float3");
pipes_cursor_tint = pipes_get_constant_location("float3");
pipes_cursor_camera_up = pipes_get_constant_location("float3");
pipes_cursor_camera_align = pipes_get_constant_location("float");
pipes_cursor_gbufferd = 0;
}
+24 -9
View File
@@ -56,15 +56,30 @@ void make_brush_run(node_shader_t *kong) {
}
node_shader_write_frag(kong, "var ba: float3 = winplast.xyz - winp.xyz;");
if (g_context->brush_lazy_radius > 0 && g_context->brush_lazy_step > 0) {
// Sphere
node_shader_write_frag(kong, "dist = distance(input.wposition, winp.xyz);");
}
else {
// Capsule
node_shader_write_frag(kong, "var h: float = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);");
node_shader_write_frag(kong, "dist = length(pa - ba * h);");
}
node_shader_add_constant(kong, "VP: float4x4", "_view_proj_matrix");
node_shader_add_constant(kong, "camera_up: float3", "_camera_up");
node_shader_add_constant(kong, "aspect_ratio: float", "_aspect_ratio_window");
node_shader_add_constant(kong, "camera_align: float", "_brush_camera_align");
node_shader_write_frag(kong, "if (constants.camera_align > 0.0) {");
node_shader_write_frag(kong, "var vp_up_y: float = (constants.VP * float4(constants.camera_up, 0.0)).y;");
node_shader_write_frag(kong, "var ca_scale: float = (1.0 / winp.w) / (vp_up_y * constants.brush_radius);");
node_shader_write_frag(kong, "var sa: float2 = sp.xy - constants.inp.xy;");
node_shader_write_frag(kong, "sa.x *= constants.aspect_ratio;");
node_shader_write_frag(kong, "sa = sa * ca_scale;");
// Capsule
node_shader_write_frag(kong, "var sb: float2 = constants.inplast.xy - constants.inp.xy;");
node_shader_write_frag(kong, "sb.x *= constants.aspect_ratio;");
node_shader_write_frag(kong, "sb = sb * ca_scale;");
node_shader_write_frag(kong, "var sh: float = clamp(dot(sa, sb) / dot(sb, sb), 0.0, 1.0);");
node_shader_write_frag(kong, "dist = length(sa - sb * sh) * 2.0 * constants.brush_radius;");
node_shader_write_frag(kong, "}");
node_shader_write_frag(kong, "else {");
// Capsule
node_shader_write_frag(kong, "var h: float = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);");
node_shader_write_frag(kong, "dist = length(pa - ba * h);");
node_shader_write_frag(kong, "}");
node_shader_write_frag(kong, "if (dist > constants.brush_radius) { discard; }");
+13 -1
View File
@@ -421,6 +421,9 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
node_shader_add_function(kong, str_octahedron_wrap);
node_shader_add_texture(kong, "gbuffer0", NULL);
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", "_brush_camera_align");
node_shader_add_constant(kong, "VP: float4x4", "_view_proj_matrix");
node_shader_write_frag(kong, "var mn0: float2 = sample_lod(gbuffer0, sampler_linear, constants.inp.xy, 0.0).rg;");
node_shader_write_frag(kong, "var mn: float3;");
node_shader_write_frag(kong, "mn.z = 1.0 - abs(mn0.x) - abs(mn0.y);");
@@ -432,7 +435,16 @@ node_shader_context_t *make_paint_run(material_t *data, material_context_t *matc
node_shader_write_frag(kong, "var mt: float3 = normalize(mr - mn * dot(mr, mn));");
node_shader_write_frag(kong, "var mb: float3 = cross(mt, mn);");
node_shader_write_frag(kong, "var pa_mask_3d: float3 = input.wposition - winp.xyz;");
node_shader_write_frag(kong, "var pa_mask: float2 = float2(dot(pa_mask_3d, mt), dot(pa_mask_3d, mb));");
node_shader_write_frag(kong, "var pa_mask: float2;");
node_shader_write_frag(kong, "if (constants.camera_align > 0.0) {");
node_shader_write_frag(kong, "var mvp_up_y: float = (constants.VP * float4(constants.camera_up, 0.0)).y;");
node_shader_write_frag(kong, "var msa: float2 = sp.xy - constants.inp.xy;");
node_shader_write_frag(kong, "msa.x *= constants.aspect_ratio;");
node_shader_write_frag(kong, "pa_mask = msa * (2.0 / (mvp_up_y * winp.w));");
node_shader_write_frag(kong, "}");
node_shader_write_frag(kong, "else {");
node_shader_write_frag(kong, "pa_mask = float2(dot(pa_mask_3d, mt), dot(pa_mask_3d, mb));");
node_shader_write_frag(kong, "}");
if (g_context->brush_directional) {
node_shader_add_constant(kong, "brush_direction: float3", "_brush_direction");
node_shader_write_frag(kong, "if (constants.brush_direction.z == 0.0) { discard; }");
+1 -1
View File
@@ -354,7 +354,7 @@ node_shader_context_t *sculpt_make_sculpt_run(material_t *data, material_context
node_shader_add_constant(kong, "aspect_ratio: float", "_aspect_ratio_window");
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_add_constant(kong, "camera_align: float", "_brush_camera_align");
node_shader_write_attrib_frag(kong, "var uvsp: float2 = float2(0.0, 0.0);");
+1 -1
View File
@@ -42,7 +42,7 @@ void make_texcoord_run(node_shader_t *kong) {
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_add_constant(kong, "camera_align: float", "_brush_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;");
+4 -1
View File
@@ -659,6 +659,9 @@ void render_path_paint_draw_cursor(f32 mx, f32 my, f32 radius, f32 tint_r, f32 t
vec4_t right = vec4_norm(camera_object_right_world(scene_camera));
gpu_set_float3(pipes_cursor_camera_right, right.x, right.y, right.z);
gpu_set_float3(pipes_cursor_tint, tint_r, tint_g, tint_b);
vec4_t up = vec4_norm(camera_object_up_world(scene_camera));
gpu_set_float3(pipes_cursor_camera_up, up.x, up.y, up.z);
gpu_set_float(pipes_cursor_camera_align, context_is_brush_camera_align() ? 1.0f : 0.0f);
gpu_set_mat4(pipes_cursor_vp, scene_camera->vp);
mat4_t inv_vp = mat4_inv(scene_camera->vp);
gpu_set_mat4(pipes_cursor_inv_vp, inv_vp);
@@ -699,7 +702,7 @@ void render_path_paint_draw_cursor_decal(f32 mx, f32 my, f32 radius, f32 opacity
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);
gpu_set_float(pipes_cursor_decal_camera_align, context_is_brush_camera_align() ? 1.0f : 0.0f);
f32 opacity = g_context->brush_opacity * brush_nodes_opacity * opacity_scale;
gpu_set_float(pipes_cursor_decal_opacity, opacity);
f32 angle = (g_context->brush_angle + brush_nodes_angle) * (math_pi() / 180.0);
+1 -1
View File
@@ -300,7 +300,7 @@ typedef struct context {
bool decal_preview;
f32 decal_x;
f32 decal_y;
bool decal_camera_align;
bool brush_camera_align;
bool select_active; // Select tool
bool select_dragging;
f32 select_start_x;
+2 -2
View File
@@ -131,8 +131,8 @@ 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, "_brush_camera_align")) {
return context_is_brush_camera_align() ? 1.0f : 0.0f;
}
else if (string_equals(link, "_picker_opacity")) {
return g_context->picked_color->opacity;