// Mawida lit shader (v0.3): one light, one dot product.
//
// The delta from sprite.wgsl: one new uniform (the directional light at
// group(2)), one new varying (the world-space normal), one new term (the
// Lambert factor). The texture-times-tint multiply from 2D is preserved
// intact underneath the light: 2D and 3D differ by a camera and a shading
// model, not by architecture.
//
// Vertex layout v2 (same buffers as the sprite pipeline):
//   location 0: position, 1: normal, 2: uv, 3: color
//   locations 4..7: instance model matrix columns, 8: tint, 9: uv_rect

struct Camera {
    view_proj: mat4x4<f32>,
};
@group(0) @binding(0) var<uniform> camera: Camera;

@group(1) @binding(0) var t_diffuse: texture_2d<f32>;
@group(1) @binding(1) var s_diffuse: sampler;

struct Light {
    direction: vec4<f32>, // xyz normalized, pointing FROM the light; w unused
    color: vec4<f32>,
};
@group(2) @binding(0) var<uniform> light: Light;

struct VertexInput {
    @location(0) position: vec3<f32>,
    @location(1) normal: vec3<f32>,
    @location(2) uv: vec2<f32>,
    @location(3) color: vec4<f32>,
};

struct InstanceInput {
    @location(4) model_0: vec4<f32>,
    @location(5) model_1: vec4<f32>,
    @location(6) model_2: vec4<f32>,
    @location(7) model_3: vec4<f32>,
    @location(8) tint: vec4<f32>,
    @location(9) uv_rect: vec4<f32>,
};

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) uv: vec2<f32>,
    @location(1) color: vec4<f32>,
    @location(2) world_normal: vec3<f32>,
};

@vertex
fn vs_main(in: VertexInput, inst: InstanceInput) -> VertexOutput {
    let model = mat4x4<f32>(inst.model_0, inst.model_1, inst.model_2, inst.model_3);
    var out: VertexOutput;
    let world = model * vec4<f32>(in.position, 1.0);
    out.clip_position = camera.view_proj * world;
    out.uv = inst.uv_rect.xy + in.uv * inst.uv_rect.zw;
    out.color = in.color * inst.tint;
    // Carry the normal to the fragment stage in world space. w = 0.0:
    // directions rotate and scale but do not translate. (Nonuniform scale
    // wants the inverse-transpose; v0.3 keeps uniform scale and says so.)
    out.world_normal = normalize((model * vec4<f32>(in.normal, 0.0)).xyz);
    return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    let tex = textureSample(t_diffuse, s_diffuse, in.uv);
    // The Lambert term scales the same color the 2D path computes.
    let n = normalize(in.world_normal);
    let lambert = max(dot(n, -light.direction.xyz), 0.0);
    let lit = light.color.rgb * lambert + vec3<f32>(0.08); // tiny ambient floor
    return vec4<f32>(tex.rgb * in.color.rgb * lit, tex.a * in.color.a);
}
