cbuffer cbEveryFrame
{
  matrix View;
  matrix Projection;

  float4 ViewEye;

  float4 LightPos[4];
  float4 LightDir[4];
  float4 LightColor[4];
};

cbuffer cbMultiplePerFrame
{
  matrix World;
};

cbuffer cbMultiplePerController
{
  matrix Joints[16];
};

cbuffer cbPerMaterial
{
  float4 Emission;
  float4 Ambient;
  float4 Diffuse;
  float4 Specular;
  float Shininess;

  int4 TextureChannel;
};

Texture2D TexEmission;
Texture2D TexAmbient;
Texture2D TexDiffuse;
Texture2D TexSpecular;

SamplerState samLinear {
  Filter = MIN_MAG_MIP_LINEAR;
  AddressU = Wrap;
  AddressV = Wrap;
};

struct VS_INPUT_SKINNED
{
  float3 Pos : POSITION;
  float3 Norm : NORMAL;
  float2 Tex : TEXCOORD0;
  int4 Joint : BLENDINDICES0;
  float4 Weight: BLENDWEIGHT0;
};

struct VS_INPUT
{
  float3 Pos : POSITION;
  float3 Norm : NORMAL;
  float2 Tex : TEXCOORD0;
};

struct PS_INPUT
{
  float4 Pos : SV_POSITION;
  float3 Norm : NORMAL;
  float2 Tex : TEXCOORD0;
  float4 WPos : POSITION;
};

struct PS_OUTPUT
{
  float4 color0 : SV_TARGET0;
  float4 color1 : SV_TARGET1;
};

PS_INPUT VSSkin(VS_INPUT_SKINNED input)
{
  PS_INPUT output;

  matrix mSkin
    = input.Weight.x * Joints[input.Joint.x]
    + input.Weight.y * Joints[input.Joint.y]
    + input.Weight.z * Joints[input.Joint.z]
    + input.Weight.w * Joints[input.Joint.w]
    ;

  float4 world_pos = mul(float4(input.Pos, 1), mSkin);
  //float4 world_pos = mul(float4(input.Pos, 1), World);
  output.Pos = mul(world_pos, View);
  output.Pos = mul(output.Pos, Projection);

  output.Norm = mul(input.Norm, (float3x3)World);
  output.Tex = input.Tex;

  output.WPos = world_pos;

  return output;
}

PS_INPUT VS(VS_INPUT input)
{
  PS_INPUT output;

  float4 world_pos = mul(float4(input.Pos, 1), World);
  output.Pos = mul(world_pos, View);
  output.Pos = mul(output.Pos, Projection);

  output.Norm = mul(input.Norm, (float3x3)World);
  output.Tex = float2(input.Tex.x, 1 - input.Tex.y);

  output.WPos = world_pos;

  return output;
}

PS_OUTPUT PS(PS_INPUT input)
{
  float3 normal = normalize(input.Norm);
  float3 view_dir = normalize(ViewEye.xyz - input.WPos.xyz);

  float3 emissionColor = float3(0, 0, 0);

  if (TextureChannel.x >= 0) { // emission
    emissionColor = TexEmission.Sample(samLinear, input.Tex).xyz;
  } else {
    emissionColor = Emission.xyz;
  }

  float3 diffuseColor;
  if (TextureChannel.z >= 0) { // diffuse
    diffuseColor = TexDiffuse.Sample(samLinear, input.Tex).xyz;
  } else {
    diffuseColor = Diffuse.xyz;
  }

  float3 color = emissionColor;
  for (int i = 0; i < 4; i++) {
    float3 light_dir = normalize(-LightDir[i].xyz);
    float diffuse_intensity = max(dot(normal, light_dir), 0.0) + 0.2;

    float distance = length(LightPos[i].xyz - input.WPos.xyz);
    float attenuation = 1.0 / (0.00000006 * distance * distance * distance);

    color += diffuseColor * diffuse_intensity * LightColor[i].xyz * attenuation;
  }

  PS_OUTPUT output;
  output.color0 = float4(color, 1.0);
  output.color1 = float4(emissionColor, 1.0);
  return output;
}

BlendState DisableBlending
{
  BlendEnable[0] = FALSE;
};

DepthStencilState EnableDepth
{
  DepthEnable = TRUE;
  DepthWriteMask = ALL;
};

technique10 Blinn
{
  pass P0
  {
    SetVertexShader(CompileShader(vs_4_0, VS()));
    SetGeometryShader(NULL);
    SetPixelShader(CompileShader(ps_4_0, PS()));
    SetBlendState(DisableBlending, float4(0.0, 0.0, 0.0, 0.0), 0xffffffff);
    SetDepthStencilState(EnableDepth, 0);
  }
}

technique10 BlinnSkin
{
  pass P0
  {
    SetVertexShader(CompileShader(vs_4_0, VSSkin()));
    SetGeometryShader(NULL);
    SetPixelShader(CompileShader(ps_4_0, PS()));
    SetBlendState(DisableBlending, float4(0.0, 0.0, 0.0, 0.0), 0xffffffff);
    SetDepthStencilState(EnableDepth, 0);
  }
}
