// Stage 4 forward lit shader: a single directional light with Lambert diffuse,
// ambient, and a Blinn-Phong specular term scaled by material roughness/metallic
// (PBR-lite). Output is linear color; an sRGB surface format converts on write.

struct Globals {
    view_proj: mat4x4<f32>,
    camera_pos: vec4<f32>,   // xyz world-space camera position
    light_dir: vec4<f32>,    // xyz unit vector pointing TOWARD the light
    light_color: vec4<f32>,  // rgb light color * intensity
    ambient: vec4<f32>,      // rgb ambient term
};

struct ObjectData {
    model: mat4x4<f32>,
    normal_mtx: mat4x4<f32>, // inverse-transpose of model (3x3 in a 4x4)
    albedo: vec4<f32>,
    mr: vec4<f32>,           // x = metallic, y = roughness
};

@group(0) @binding(0) var<uniform> globals: Globals;
@group(1) @binding(0) var<uniform> obj: ObjectData;

struct VsOut {
    @builtin(position) clip_pos: vec4<f32>,
    @location(0) world_pos: vec3<f32>,
    @location(1) world_normal: vec3<f32>,
    @location(2) uv: vec2<f32>,
};

@vertex
fn vs_main(
    @location(0) position: vec3<f32>,
    @location(1) normal: vec3<f32>,
    @location(2) uv: vec2<f32>,
) -> VsOut {
    let world = obj.model * vec4<f32>(position, 1.0);
    var out: VsOut;
    out.world_pos = world.xyz;
    out.world_normal = (obj.normal_mtx * vec4<f32>(normal, 0.0)).xyz;
    out.uv = uv;
    out.clip_pos = globals.view_proj * world;
    return out;
}

@fragment
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
    let n = normalize(in.world_normal);
    let l = normalize(globals.light_dir.xyz);
    let v = normalize(globals.camera_pos.xyz - in.world_pos);
    let h = normalize(l + v);

    let albedo = obj.albedo.rgb;
    let metallic = obj.mr.x;
    let roughness = clamp(obj.mr.y, 0.04, 1.0);

    let ndl = max(dot(n, l), 0.0);
    let ndh = max(dot(n, h), 0.0);

    // Metals have no diffuse; dielectrics get a fixed 0.04 specular, metals
    // tint their specular by the albedo.
    let diffuse = albedo * (1.0 - metallic);
    let spec_color = mix(vec3<f32>(0.04), albedo, metallic);
    let spec_power = mix(8.0, 256.0, 1.0 - roughness);
    let spec = spec_color * pow(ndh, spec_power) * select(0.0, 1.0, ndl > 0.0);

    let direct = (diffuse * ndl + spec) * globals.light_color.rgb;
    let ambient = albedo * globals.ambient.rgb;
    return vec4<f32>(ambient + direct, obj.albedo.a);
}
