#version 430

#include "common/compute.h"

const int MAX_INFLUENCES = 4;

BEGIN_DRAW_TEXTURES
	NO_DRAW_TEXTURES
END_DRAW_TEXTURES

BEGIN_DRAW_UNIFORMS
	UNIFORM(float, vertexCount)
	// offset.x - input vertex offset
	// offset.y - input skinning data offset
	// offset.z - output position offset
	// offset.w - output packed normal and tangent offset
	UNIFORM(vec4, offset)
END_DRAW_UNIFORMS

BEGIN_UNIFORM_BUFFER(SkinningMatrices)
	mat4 skinBlendMatrices[MAX_BONES];
END_UNIFORM_BUFFER

struct Vertex
{
	vec4 tangent;
	vec4 normal;
	vec4 position;
};

STORAGE_BUFFER(0) restrict readonly buffer InputVertices
{
	Vertex vertices[];
};

STORAGE_BUFFER(1) restrict readonly buffer SkinData
{
	uvec2 skinDatas[];
};

STORAGE_BUFFER(2) writeonly buffer OutputPositions
{
	vec4 outputPositions[];
};

STORAGE_BUFFER(3) writeonly buffer OutputNormalsTangents
{
	uvec4 outputPackedNT[];
};

uvec4 getSkinJoints(in uvec2 skinData)
{
	return uvec4(unpackUnorm4x8(skinData.y) * 255.0);
}

vec4 getSkinWeights(in uvec2 skinData)
{
	return unpackUnorm4x8(skinData.x);
}

uvec4 packNormalTangent(in vec3 normal, in vec3 tangent, in float weight)
{
	return uvec4(
		packHalf2x16(normal.xy), packHalf2x16(vec2(normal.z, 0.0)),
		packHalf2x16(tangent.xy), packHalf2x16(vec2(tangent.z, weight)));
}

layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
void main()
{
	uint index = gl_GlobalInvocationID.x;
	if (index >= vertexCount)
		return;

	uvec4 offsets = uvec4(offset);

	Vertex vertex = vertices[index + offsets[0]];
	uvec2 skinData = skinDatas[index + offsets[1]];

	vec3 localPosition = vertex.position.xyz;
	vec3 localNormal = vertex.normal.xyz;
	vec3 localTangent = vertex.tangent.xyz;
	uvec4 skinJoints = getSkinJoints(skinData);
	vec4 skinWeights = getSkinWeights(skinData);

	vec3 transformedPosition = vec3(0.0);
	vec3 transformedNormal = vec3(0.0);
	vec3 transformedTangent = vec3(0.0);
	for (uint i = 0; i < MAX_INFLUENCES; ++i)
	{
		uint joint = skinJoints[i];
		if (joint != 0xFF)
		{
			mat4 matrix = skinBlendMatrices[joint];
			transformedPosition += vec3(matrix * vec4(localPosition, 1.0)) * skinWeights[i];
			transformedNormal += vec3(matrix * vec4(localNormal, 0.0)) * skinWeights[i];
			transformedTangent += vec3(matrix * vec4(localTangent, 0.0)) * skinWeights[i];
		}
	}
	transformedNormal = normalize(transformedNormal);
	transformedTangent = normalize(transformedTangent);

	outputPositions[index + offsets[2]] = vec4(transformedPosition, 0.0);
	outputPackedNT[index + offsets[3]] = packNormalTangent(transformedNormal, transformedTangent, vertex.tangent.w);
}
