#pragma kernel CSCullIndividual
#pragma kernel CSCullCell

struct GrassElement {
    float3 position;
    float rotation;
    float size;
    float colorBlend;//0-1
    };

struct grassCell {
    uint grassElementIndexes[512];//map to grass elements
    };

//struct grassOcclusionCell {
//    uint grassCellIndex;
//    uint visibleCellIndexes[2048];//Map to occlusion cells
//    };

//General
uint grassCount;
float2 distanceBounts;//Start, end
Texture2D<float4> ditherTex;
int ditherTexSizeX;
float ditherSize;

//Input
StructuredBuffer<GrassElement> grassBuffer;

//Frustrum culling
float3 cameraPos;
float4x3 cameraFrustumNormals;

//settings
int doOcclusion;

//Cells
uint grassCellCount;
uint grassCellSize;
StructuredBuffer<grassCell> grassCellBuffer;

//Occlusion culling
//StructuredBuffer<grassOcclusionCell> grassOcclusionBuffer;

//Output
AppendStructuredBuffer<uint> outGrassBuffer;
AppendStructuredBuffer<uint> outCellIndexBuffer;

bool Is3DDither (float dist, float2 pos) {
    float distAmount = 1 - (dist-distanceBounts.x)/(distanceBounts.y-distanceBounts.x);//0-1
    float2 relativePos = pos/ditherSize;
    float2 positivePos = float2(relativePos.x*sign(relativePos.x),relativePos.y*sign(relativePos.y));
    float2 repeatingPos = float2(fmod(positivePos.x,ditherTexSizeX),fmod(positivePos.y,ditherTexSizeX));
    float dit = ditherTex[repeatingPos].x;
    return distAmount - dit > 0;
    }

[numthreads(16,16,4)]
void CSCullIndividual (uint index : SV_GroupIndex, uint3 group : SV_GroupID) {//This only does frustrum culling
    //1. get index
    uint realIndex = index + 1024 * group.x;//id.x + id.y group.x + id.z group.x * group.y

    //2. index valid?
    if(realIndex < grassCount) {
        GrassElement data = grassBuffer[realIndex];
        float3 pos = data.position;

        float3 posFromCamera = pos - cameraPos;
        float scale = data.size;

        //3. frustrum culling
        if( (dot(cameraFrustumNormals[0], posFromCamera) > -scale) &&
            (dot(cameraFrustumNormals[1], posFromCamera) > -scale) &&
            (dot(cameraFrustumNormals[2], posFromCamera) > -scale) &&
            (dot(cameraFrustumNormals[3], posFromCamera) > -scale) ){
            //3D dither culling (includes dist culling)
            float dist = length(posFromCamera);
            if(Is3DDither(dist,pos.xz)) {
                outGrassBuffer.Append(realIndex);
                }
            }
        }
    }

[numthreads(8,8,1)]
void CSCullCell (uint index : SV_GroupIndex, uint3 group : SV_GroupID) {//This does frustrum and occlusion culling on cells
    //1. get index
    uint realIndex = index + 8*8*group.x;
    //2. index valid?

    if(realIndex < grassCellCount) {
        //outGrassBuffer.Append(realIndex);
        //outCellIndexBuffer.Append(realIndex);
        grassCell cell = grassCellBuffer[realIndex];
        
        //for(int i=0;i<512;i++) {
        //    int k = cell.grassElementIndexes[i];
        //    if(k != 0)outGrassBuffer.Append(k);
        //    }
        
        //Doing frustrum check with ranodom element from cell
        float3 pos = grassBuffer[cell.grassElementIndexes[0]].position;
        
        float3 posFromCamera = pos - cameraPos;
        float scale = grassCellSize;
        
        //3. frustrum culling
        if( (dot(cameraFrustumNormals[0], posFromCamera) > -scale) &&
            (dot(cameraFrustumNormals[1], posFromCamera) > -scale) &&
            (dot(cameraFrustumNormals[2], posFromCamera) > -scale) &&
            (dot(cameraFrustumNormals[3], posFromCamera) > -scale) ){

            if(length(posFromCamera) <= distanceBounts.y) {//Dist check
                if(doOcclusion == 1) {
                    //TODO see if this chunk is visible from current chunk
                    //TODO if yes then append chunk grass indexes
                    }
                else {
                    outCellIndexBuffer.Append(realIndex);
                    }
                }
            }
        }
    }

//group -> thread
//SV_GroupThreadID = current threads num.xyz
//SV_GroupIndex = num.x * num.y * num.z;
//SV_GroupID = current gorup indices


/* This works for some reason
[numthreads(8,8,8)]
void Converter (uint callIndex : SV_GroupIndex, uint3 groupID : SV_GroupID) {
    uint realIndex = groupID.x + groupID.y + groupID.z;

    uint cellIndex = grassCellIndexBuffer[realIndex];

    grassCell cell = grassCellBuffer[ cellIndex ];

    int elementIndex = callIndex;//Correct
    uint objIndex = cell.grassElementIndexes[elementIndex];
    if(objIndex != 0) outGrassBuffer.Append(objIndex);
    
    }
*/