#include "lib/shared/point.hlsl"
#include "lib/shared/quat-functions.hlsl"
#include "lib/shared/noise-functions.hlsl"
#include "lib/shared/hash-functions.hlsl"

cbuffer Params : register(b0)
{
    float Variation;
    float NoiseAmount;
    float Frequency;
    float Phase;    

    float3 NoiseDistribution;
    float RotationLookupDistance;

    float Length;
    float Width;
    float NoiseDensity;
}

StructuredBuffer<Point> PointsA : t0;
StructuredBuffer<Point> PointsB : t1;

Texture2D<float4> GrowthMap : register(t2);
sampler texSampler : register(s0);

RWStructuredBuffer<Point> ResultPoints : u0;


float3 GetNoise(float3 pos) 
{
    float3 noiseLookup = (pos + Phase ) * Frequency;
    return snoiseVec3(noiseLookup) * NoiseAmount * NoiseDistribution;
}

void GetTranslationAndRotation(float weight, float3 pointPos, float4 rotation, 
                               out float3 offset, out float4 newRotation) 
{    
    offset = GetNoise(pointPos) * weight;

    float3 xDir = qRotateVec3(float3(RotationLookupDistance,0,0), rotation);
    float3 offsetAtPosXDir = GetNoise(pointPos + xDir) * weight;
    float3 rotatedXDir = (pointPos + xDir + offsetAtPosXDir) - (pointPos + offset);

    float3 yDir = qRotateVec3(float3(0, RotationLookupDistance,0), rotation);
    float3 offsetAtPosYDir = GetNoise(pointPos + yDir) * weight;
    float3 rotatedYDir = (pointPos + yDir + offsetAtPosYDir) - (pointPos + offset);

    float3 rotatedXDirNormalized = normalize(rotatedXDir);
    float3 rotatedYDirNormalized = normalize(rotatedYDir);
    
    float3 crossXY = cross(rotatedXDirNormalized, rotatedYDirNormalized);
    float3x3 orientationDest= float3x3(
        rotatedXDirNormalized, 
        cross(crossXY, rotatedXDirNormalized), 
        crossXY );

    newRotation = normalize(qFromMatrix3(transpose(orientationDest)));
}



[numthreads(64,1,1)]
void main(uint3 i : SV_DispatchThreadID)
{
    uint sourceCount, strideA;
    PointsA.GetDimensions(sourceCount, strideA);
    sourceCount +=1; //For NaN-Separator

    uint targetPosCount, strideB;
    PointsB.GetDimensions(targetPosCount, strideB);

    //uint sourceCount = (uint)(CountA + 0.1);  
    uint sourceIndex = i.x % sourceCount;
    
    if(sourceIndex == sourceCount-1) 
    {
        ResultPoints[i.x].W = sqrt(-1);
    }
    else 
    {
        uint targetIndex = (i.x / sourceCount )  % targetPosCount;
        Point A = PointsA[sourceIndex];
        Point B = PointsB[targetIndex];

        //ResultPoints[i.x] = A;
        //return;

        float3  pLocal = qRotateVec3(A.Position, B.Rotation);

        float age = B.W;
        float w = A.W;

        float4 attributes = GrowthMap.SampleLevel(texSampler, float2(age,1-w), 0);
        float d = saturate(attributes.r - 0.05);
        if(d < 0.001)
            d = sqrt(-1);

        float4 rotation = qMul(A.Rotation, B.Rotation);

        float noiseWeight = attributes.g;
        float3 offset;
        float4 newRotation;
        float3 variationOffset = hash31(targetIndex) * Variation;

        GetTranslationAndRotation(noiseWeight, pLocal * NoiseDensity + variationOffset, rotation, offset, newRotation);

        ResultPoints[i.x].Position = pLocal * Length + B.Position + offset;

        ResultPoints[i.x].W = d * Width;
        ResultPoints[i.x].Rotation = newRotation;
    }
}
