#include "common.slang"

struct VertexShaderInput {
    // Per Vertex
    uint vertex_id : SV_VertexID;
    float3 pos;

    // Per Instance
    uint instance_id : SV_InstanceID;
    uint tile_info;
};

struct VertexShaderOutput {
    float4 pos : SV_POSITION;
    float2 uv;
};

struct PixelShaderOutput {
	float4 color : SV_TARGET;
};

[[vk::binding(0, 0)]]
StructuredBuffer<float4> tile_infos;

[shader("vertex")]
VertexShaderOutput main_vertex(VertexShaderInput input) {
    VertexShaderOutput output;
    float2 tile_pos = float2(input.instance_id % map_width, float(input.instance_id / map_width));
    tile_pos -= float2(0.5, 0.5);

    uint tile_type = input.tile_info & 0xffff;
    uint rotation  = (input.tile_info >> 16) & 0x3;

    float4x4 view_matrix = view(float3(camera_x, camera_y, 0), radians(camera_tilt), camera_distance);
    float4x4 projection_matrix = projection(radians(fovy_degrees), aspect_ratio, NEAR_PLANE);
    float4x4 view_projection_matrix = mul(projection_matrix, view_matrix);

    output.pos = mul(view_projection_matrix, float4(tile_pos + input.pos.xy, 0, 1));

    float4 uv_min_max = tile_infos.Load(tile_type);

    switch (rotation) {
        case 0: switch (input.vertex_id) {
            case 0: output.uv = uv_min_max.xy; break;
            case 1: output.uv = uv_min_max.zy; break;
            case 2: output.uv = uv_min_max.zw; break;
            case 3: output.uv = uv_min_max.xw; break;
        } break;

        case 1: switch (input.vertex_id) {
            case 0: output.uv = uv_min_max.zy; break;
            case 1: output.uv = uv_min_max.zw; break;
            case 2: output.uv = uv_min_max.xw; break;
            case 3: output.uv = uv_min_max.xy; break;
        } break;

        case 2: switch (input.vertex_id) {
            case 0: output.uv = uv_min_max.zw; break;
            case 1: output.uv = uv_min_max.xw; break;
            case 2: output.uv = uv_min_max.xy; break;
            case 3: output.uv = uv_min_max.zy; break;
        } break;

        case 3: switch (input.vertex_id) {
            case 0: output.uv = uv_min_max.xw; break;
            case 1: output.uv = uv_min_max.xy; break;
            case 2: output.uv = uv_min_max.zy; break;
            case 3: output.uv = uv_min_max.zw; break;
        } break;
    }

    return output;
}

[[vk::binding(0, 2)]]
Sampler2D<float4> tex1;

[shader("fragment")]
PixelShaderOutput main_fragment(VertexShaderOutput input) {
	PixelShaderOutput output;

	output.color = tex1.Sample(float2(input.uv));
	
	return output;
}
