#version 460
#extension GL_GOOGLE_include_directive : require

#include "lovr.glsl"

layout(local_size_x = 32, local_size_x_id = 0) in;

layout(push_constant) uniform PushConstants {
  uint baseVertex;
  uint vertexCount;
  uint blendShapeCount;
  uint baseBlendVertex;
  bool first;
};

struct ModelVertex {
  float px, py, pz;
  uint normal;
  float u, v;
  uint color;
  uint tangent;
};

struct BlendVertex {
  float px, py, pz;
  float nx, ny, nz;
  float tx, ty, tz;
};

layout(set = 0, binding = 0) buffer restrict readonly RawVertices { ModelVertex rawVertices[]; };
layout(set = 0, binding = 1) buffer restrict Vertices { ModelVertex vertices[]; };
layout(set = 0, binding = 2) buffer restrict readonly BlendVertices { BlendVertex blendVertex[]; };
layout(set = 0, binding = 3) uniform Weights { vec4 weights[16]; };

void lovrmain() {
  if (GlobalThreadID.x >= vertexCount) return;
  uint vertexIndex = baseVertex + GlobalThreadID.x;
  uint blendVertexIndex = baseBlendVertex + GlobalThreadID.x;

  ModelVertex vertex = first ? rawVertices[vertexIndex] : vertices[vertexIndex];

  vec4 normal = unpackSnorm10x3(vertex.normal);
  vec4 tangent = unpackSnorm10x3(vertex.tangent);

  for (uint i = 0; i < blendShapeCount; i++, blendVertexIndex += vertexCount) {
    float weight = weights[i / 4][i % 4];

    if (weight == 0.) {
      continue;
    }

    BlendVertex blendShape = blendVertex[blendVertexIndex];

    vertex.px += blendShape.px * weight;
    vertex.py += blendShape.py * weight;
    vertex.pz += blendShape.pz * weight;

    normal.x += blendShape.nx * weight;
    normal.y += blendShape.ny * weight;
    normal.z += blendShape.nz * weight;

    tangent.x += blendShape.tx * weight;
    tangent.y += blendShape.ty * weight;
    tangent.z += blendShape.tz * weight;
  }

  vertex.normal = packSnorm10x3(normal);
  vertex.tangent = packSnorm10x3(tangent);

  vertices[vertexIndex] = vertex;
}
