// RTStress closest-hit (runs in SHADE). Computes flat-shaded Lambert from
// the hit triangle's geometric normal, accumulates ambient, and — if the
// surface faces the sun — emits a shadow ray toward the sun. The shadow
// ray's miss (sun visible) adds the direct term; its hit (occluded) adds
// nothing because RT_FLAG_SKIP_CLOSEST_HIT suppresses closesthit on hit.
//
// Payload declared here so the assembler sees it before wfPayload / SHADE.
struct Payload {
    color:     vec3<f32>,   // shadow ray: pending direct contribution
    shadowRay: u32,         // 0 primary, 1 shadow
};

const SUN_DIR_TO_LIGHT: vec3<f32> = vec3<f32>(0.40, 0.85, 0.35);
const SUN_COLOR:        vec3<f32> = vec3<f32>(1.15, 1.05, 0.90);
const AMBIENT_COLOR:    vec3<f32> = vec3<f32>(0.12, 0.13, 0.18);

// Cheap per-instance albedo so the grid reads as distinct cubes (and any
// TLAS flicker as instance count scales is obvious).
fn instanceAlbedo(i: u32) -> vec3<f32> {
    let h = i * 2654435761u;
    return vec3<f32>(
        0.35 + 0.6 * f32((h >>  0u) & 255u) / 255.0,
        0.35 + 0.6 * f32((h >>  8u) & 255u) / 255.0,
        0.35 + 0.6 * f32((h >> 16u) & 255u) / 255.0);
}

fn closesthit_main(ray: RayDesc, hit: HitInfo, payload: ptr<function, Payload>) {
    let meshRec = meshRecords[tlasEntries[hit.instanceId].blasMeshIdx];
    let verts = _rtFetchTri(meshRec, hit.primitiveId);
    let nObj = normalize(cross(verts[1] - verts[0], verts[2] - verts[0]));
    let nWorld = normalize(vec3<f32>(
        dot(hit.objectToWorldR0.xyz, nObj),
        dot(hit.objectToWorldR1.xyz, nObj),
        dot(hit.objectToWorldR2.xyz, nObj)));

    let albedo   = instanceAlbedo(hit.customIndex);
    let worldPos = ray.origin + ray.direction * hit.t;
    let viewDir  = -ray.direction;
    let nFacing  = select(-nWorld, nWorld, dot(nWorld, viewDir) > 0.0);
    let sunDir   = normalize(SUN_DIR_TO_LIGHT);
    let nDotL    = max(0.0, dot(nFacing, sunDir));

    rtAccumulate(albedo * AMBIENT_COLOR);

    if (nDotL > 0.0) {
        var sp: Payload;
        sp.color     = albedo * SUN_COLOR * nDotL;
        sp.shadowRay = 1u;
        let shadowOrigin = worldPos + nFacing * 0.05;
        rtEmitRay(shadowOrigin, 0.01, sunDir, 100000.0,
                  RT_FLAG_SKIP_CLOSEST_HIT | RT_FLAG_TERMINATE_ON_FIRST_HIT,
                  0xFFu, 0u, 0u, sp);
    }
}
