// BVLE Voxels - Shared shader definitions
// Root signature, common structures, and constant buffers for voxel shaders.

#ifndef VOXEL_COMMON_HLSLI
#define VOXEL_COMMON_HLSLI

// Wicked Engine DX12 root signature (HLSL 6.6+ bindless model)
//   b999:  push constants (12 x uint32 = 48 bytes)
//   b0-b2: root CBV descriptors
//   t0-t15, u0-u15: SRV/UAV descriptor table
//   s0-s7:  dynamic samplers
//   s100+:  static samplers
#define VOXEL_ROOTSIG \
    "RootFlags(CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED | SAMPLER_HEAP_DIRECTLY_INDEXED), " \
    "RootConstants(num32BitConstants=12, b999), " \
    "CBV(b0), " \
    "CBV(b1), " \
    "CBV(b2), " \
    "DescriptorTable( " \
        "CBV(b3, numDescriptors = 11, flags = DATA_STATIC_WHILE_SET_AT_EXECUTE)," \
        "SRV(t0, numDescriptors = 16, flags = DESCRIPTORS_VOLATILE | DATA_STATIC_WHILE_SET_AT_EXECUTE)," \
        "UAV(u0, numDescriptors = 16, flags = DESCRIPTORS_VOLATILE | DATA_STATIC_WHILE_SET_AT_EXECUTE)" \
    ")," \
    "DescriptorTable( " \
        "Sampler(s0, offset = 0, numDescriptors = 8, flags = DESCRIPTORS_VOLATILE)" \
    ")," \
    "StaticSampler(s100, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
    "StaticSampler(s101, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
    "StaticSampler(s102, addressU = TEXTURE_ADDRESS_MIRROR, addressV = TEXTURE_ADDRESS_MIRROR, addressW = TEXTURE_ADDRESS_MIRROR, filter = FILTER_MIN_MAG_MIP_LINEAR)," \
    "StaticSampler(s103, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_MIN_MAG_MIP_POINT)," \
    "StaticSampler(s104, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_MIN_MAG_MIP_POINT)," \
    "StaticSampler(s105, addressU = TEXTURE_ADDRESS_MIRROR, addressV = TEXTURE_ADDRESS_MIRROR, addressW = TEXTURE_ADDRESS_MIRROR, filter = FILTER_MIN_MAG_MIP_POINT)," \
    "StaticSampler(s106, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_ANISOTROPIC, maxAnisotropy = 16)," \
    "StaticSampler(s107, addressU = TEXTURE_ADDRESS_WRAP, addressV = TEXTURE_ADDRESS_WRAP, addressW = TEXTURE_ADDRESS_WRAP, filter = FILTER_ANISOTROPIC, maxAnisotropy = 16)," \
    "StaticSampler(s108, addressU = TEXTURE_ADDRESS_MIRROR, addressV = TEXTURE_ADDRESS_MIRROR, addressW = TEXTURE_ADDRESS_MIRROR, filter = FILTER_ANISOTROPIC, maxAnisotropy = 16)," \
    "StaticSampler(s109, addressU = TEXTURE_ADDRESS_CLAMP, addressV = TEXTURE_ADDRESS_CLAMP, addressW = TEXTURE_ADDRESS_CLAMP, filter = FILTER_COMPARISON_MIN_MAG_LINEAR_MIP_POINT, comparisonFunc = COMPARISON_GREATER_EQUAL),"

// ── Per-frame constant buffer (b0) ──────────────────────────────
cbuffer VoxelCB : register(b0) {
    float4x4 viewProjection;
    float4x4 inverseViewProjection; // for depth-to-world reconstruction (RT shadows)
    float4x4 prevViewProjection;    // previous frame's VP for temporal reprojection (RT AO)
    float4 cameraPosition;
    float4 sunDirection;
    float4 sunColor;
    float chunkSize;
    float textureTiling;
    float blendEnabled;
    float debugBlend; // >0.5 = show blend zones as debug colors
    // Frustum culling data (used by cull compute shader)
    float4 frustumPlanes[6]; // ax+by+cz+d=0, xyz=normal, w=distance
    uint chunkCount;
    uint bleedMask;       // bit N set = material N can bleed onto neighbors
    uint resistBleedMask; // bit N set = material N resists bleed from neighbors
    float windTime;  // elapsed time for wind animation (seconds)
    // ── Stylized lighting (Phase 7) ──
    float4 skyAmbient;       // hemisphere ambient: sky (top) color
    float4 groundAmbient;    // hemisphere ambient: ground (bottom) color
    float4 shadowTint;       // colored shadow tint (blue-violet)
    float4 fogColor;         // atmospheric fog color
    float4 fogParams;        // x=density, y=unused, z=unused, w=unused
    float4 rimColor;         // rim/fresnel light color
    float4 rimParams;        // x=exponent, y=intensity, z=unused, w=unused
    float4 toneMapParams;    // x=saturation boost, y=exposure, z=unused, w=unused
};

// ── Indirect draw args (must match C++ IndirectDrawArgs, 20 bytes) ──
// Wicked Engine's DrawInstancedIndirectCount command signature includes a push
// constant (1 × uint32 at b999[0]) BEFORE each D3D12_DRAW_ARGUMENTS.
// Total stride = 4 + 16 = 20 bytes per draw entry.
struct IndirectDrawArgsInstanced {
    uint pushConstant;              // written to b999[0] by ExecuteIndirect
    uint vertexCountPerInstance;
    uint instanceCount;
    uint startVertexLocation;
    uint startInstanceLocation;
};

// ── GPU chunk info (must match C++ GPUChunkInfo, 112 bytes) ─────
// NOTE: No arrays — scalar-only to guarantee C-style packing in StructuredBuffer.
struct GPUChunkInfo {
    float4 worldPos;    // xyz = chunk origin in world space, w = debug flag
    uint quadOffset;    // offset into mega quad buffer (in quads)
    uint quadCount;     // number of quads for this chunk
    uint _pad0;
    uint _pad1;
    // Per-face data (6 faces: +X -X +Y -Y +Z -Z)
    uint faceOff0, faceOff1, faceOff2, faceOff3, faceOff4, faceOff5;
    uint faceCnt0, faceCnt1, faceCnt2, faceCnt3, faceCnt4, faceCnt5;
    // Face neighbor chunk indices (0xFFFFFFFF = no neighbor)
    uint neighbor0, neighbor1, neighbor2, neighbor3, neighbor4, neighbor5; // +X,-X,+Y,-Y,+Z,-Z
    uint _pad2, _pad3;
};

// Helper functions to access scalar face fields by index
uint getFaceOffset(GPUChunkInfo info, uint f) {
    switch (f) {
        case 0: return info.faceOff0;
        case 1: return info.faceOff1;
        case 2: return info.faceOff2;
        case 3: return info.faceOff3;
        case 4: return info.faceOff4;
        default: return info.faceOff5;
    }
}

uint getFaceCount(GPUChunkInfo info, uint f) {
    switch (f) {
        case 0: return info.faceCnt0;
        case 1: return info.faceCnt1;
        case 2: return info.faceCnt2;
        case 3: return info.faceCnt3;
        case 4: return info.faceCnt4;
        default: return info.faceCnt5;
    }
}

uint getNeighborIdx(GPUChunkInfo info, uint f) {
    switch (f) {
        case 0: return info.neighbor0; // +X
        case 1: return info.neighbor1; // -X
        case 2: return info.neighbor2; // +Y
        case 3: return info.neighbor3; // -Y
        case 4: return info.neighbor4; // +Z
        default: return info.neighbor5; // -Z
    }
}

#endif // VOXEL_COMMON_HLSLI
