//******************************************************************************
//
// OGL Utility / OGLShaders
//
// シェーダ定義
//
// Copyright (C) 2022 WADA Masashi. All Rights Reserved.
//
//******************************************************************************

#import "OGLShaders.h"
#include <metal_stdlib>
#include <metal_matrix>

using namespace metal;


//******************************************************************************
// パラメータ定義
//******************************************************************************
//------------------------------------------------------------------------------
// 入力パラメータ定義：頂点構造体
//------------------------------------------------------------------------------
struct InVertexV3N3C {
	packed_float3 position;      //[[attribute(VertexV3N3C_Attribute_Position)]];        //0
	packed_float3 normal;        //[[attribute(VertexV3N3C_Attribute_Normal)]];          //1
	packed_uchar4 color;         //[[attribute(VertexV3N3C_Attribute_Color)]];           //2
};

struct InVertexV3CT2 {
	packed_float3 position;      //[[attribute(VertexV3CT2_Attribute_Position)]];        //0
	packed_uchar4 color;         //[[attribute(VertexV3CT2_Attribute_Color)]];           //1
	packed_float2 textureCoords; //[[attribute(VertexV3CT2_Attribute_TextureCoords)]];   //2
};

struct InVertexV3N3CT2 {
	packed_float3 position;      //[[attribute(VertexV3N3CT2_Attribute_Position)]];      //0
	packed_float3 normal;        //[[attribute(VertexV3N3CT2_Attribute_Normal)]];        //1
	packed_uchar4 color;         //[[attribute(VertexV3N3CT2_Attribute_Color)]];         //2
	packed_float2 textureCoords; //[[attribute(VertexV3N3CT2_Attribute_TextureCoords)]]; //3
};

//------------------------------------------------------------------------------
// Vertex関数出力パラメータ / Fragment関数入力パラメータ 定義
//------------------------------------------------------------------------------
struct ProjectedVertexV3N3C {
	float4 position [[position]];
	float4 normal;
	float4 eye;
	float4 diffuseColor;
	float pointSize [[point_size]];
};

struct ProjectedVertexV3CT2 {
	float4 position [[position]];
	float4 diffuseColor;
	float2 textureCoords;
};

struct ProjectedVertexV3N3CT2 {
	float4 position [[position]];
	float4 normal;
	float4 eye;
	float4 diffuseColor;
	float2 textureCoords;
};

//******************************************************************************
// 関数定義
//******************************************************************************
//ディレクショナルライト色計算
float3 dirLightColor(
				OGLMaterial material,	//マテリアル
				OGLLight light,			//ライト：フラグメントから光源への方向ベクトルを持つ
				float3 normal,			//法線ベクトル
				float3 eyeDir			//フラグメントから視点へのベクトル
			);


//******************************************************************************
// 頂点関数 V3N3C
//******************************************************************************
vertex ProjectedVertexV3N3C vertex_V3N3C(
		const device InVertexV3N3C *vertices	[[buffer(BufferIndex_Vertices)]], //0
		constant OGLUniforms &uniforms			[[buffer(BufferIndex_Uniforms)]], //1
		uint vid [[vertex_id]]
	)
{
	ProjectedVertexV3N3C outVertex;
	float4 in_position;
	float4 in_normal;
	packed_uchar4 in_rgba;
	
	//座標
	in_position = float4(vertices[vid].position, 1.0);
	outVertex.position = uniforms.modelViewProjectionMatrix * in_position;
	
	//法線
	in_normal = float4(vertices[vid].normal, 1.0);
	
	outVertex.normal = uniforms.normalMatrix * in_normal;
	
	//視線方向
	outVertex.eye = -(uniforms.modelViewMatrix * in_position);
	
	//色
	in_rgba = vertices[vid].color;
	outVertex.diffuseColor = float4(float(in_rgba[0])/255,	//R
									float(in_rgba[1])/255,	//G
									float(in_rgba[2])/255,	//B
									float(in_rgba[3])/255);	//A
	
	//点のサイズ：プリミティブ種別が点の場合に参照される
	outVertex.pointSize = uniforms.pointSize;
	
	return outVertex;
}

//******************************************************************************
// フラグメント関数 V3N3C
//******************************************************************************
fragment float4 fragment_V3N3C(
		ProjectedVertexV3N3C vert [[stage_in]],
		constant OGLUniforms &uniforms			[[buffer(BufferIndex_Uniforms)]] //1
	)
{
	OGLMaterial material;
	float3 color0;
	float3 color1;
	float4 outColor;
	
	//マテリアル拡散光を頂点色で上書きする
	material = uniforms.material;
	material.diffuseColor = vert.diffuseColor.xyz;
	
	if (uniforms.enableLights == 0) {
		//ライト無効
		outColor = vert.diffuseColor;
	}
	else {
		//ライト有効
		color0 = dirLightColor(material, uniforms.light0, vert.normal.xyz, vert.eye.xyz);
		color1 = dirLightColor(material, uniforms.light1, vert.normal.xyz, vert.eye.xyz);
		outColor = float4(color0 + color1, vert.diffuseColor.a);
	}
	
	return outColor;
}

//******************************************************************************
// 頂点関数 V3CT2
//******************************************************************************
vertex ProjectedVertexV3CT2 vertex_V3CT2(
		const device InVertexV3CT2 *vertices	[[buffer(BufferIndex_Vertices)]], //0
		constant OGLUniforms &uniforms			[[buffer(BufferIndex_Uniforms)]], //1
		uint vid [[vertex_id]]
	)
{
	ProjectedVertexV3CT2 outVertex;
	float4 in_position;
	packed_uchar4 in_rgba;
	
	//座標
	in_position = float4(vertices[vid].position, 1.0);
	outVertex.position = uniforms.modelViewProjectionMatrix * in_position;
	
	//色
	in_rgba = vertices[vid].color;
	outVertex.diffuseColor = float4(float(in_rgba[0])/255,	//R
									float(in_rgba[1])/255,	//G
									float(in_rgba[2])/255,	//B
									float(in_rgba[3])/255);	//A
	
	//UV座標
	outVertex.textureCoords = vertices[vid].textureCoords;
	
	return outVertex;
}

//******************************************************************************
// フラグメント関数 V3CT2
//******************************************************************************
fragment float4 fragment_V3CT2(
		ProjectedVertexV3CT2 vert				[[stage_in]],
		texture2d<float> texture				[[texture(TextureIndex0)]] //0
	)
{
	float4 vertexColor;
	float4 textureSampleColor;
	float4 outColor;
	constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
	
	//頂点色
	vertexColor = vert.diffuseColor;
	
	//テクスチャサンプリング色
	textureSampleColor = texture.sample(textureSampler, vert.textureCoords);
	
	//出力色
	outColor = textureSampleColor * vertexColor;
	
	return outColor;
}

//******************************************************************************
// 頂点関数 V3N3CT2
//******************************************************************************
vertex ProjectedVertexV3N3CT2 vertex_V3N3CT2(
		const device InVertexV3N3CT2 *vertices	[[buffer(BufferIndex_Vertices)]], //0
		constant OGLUniforms &uniforms			[[buffer(BufferIndex_Uniforms)]], //1
		uint vid [[vertex_id]]
	)
{
	ProjectedVertexV3N3CT2 outVertex;
	float4 in_position;
	float4 in_normal;
	packed_uchar4 in_rgba;
	
	//座標
	in_position = float4(vertices[vid].position, 1.0);
	outVertex.position = uniforms.modelViewProjectionMatrix * in_position;
	
	//法線
	in_normal = float4(vertices[vid].normal, 1.0);
	outVertex.normal = uniforms.normalMatrix * in_normal;
	
	//視線方向
	outVertex.eye = -(uniforms.modelViewMatrix * in_position);
	
	//色
	in_rgba = vertices[vid].color;
	outVertex.diffuseColor = float4(float(in_rgba[0])/255.0,	//R
									float(in_rgba[1])/255.0,	//G
									float(in_rgba[2])/255.0,	//B
									float(in_rgba[3])/255.0);	//A
		
	//UV座標
	outVertex.textureCoords = vertices[vid].textureCoords;
	
	return outVertex;
}

//******************************************************************************
// フラグメント関数 V3N3CT2
//******************************************************************************
fragment float4 fragment_V3N3CT2(
		ProjectedVertexV3N3CT2 vert				[[stage_in]],
		constant OGLUniforms &uniforms			[[buffer(BufferIndex_Uniforms)]], //1
		texture2d<float> texture				[[texture(TextureIndex0)]]        //0
	 )
{
	OGLMaterial material;
	float3 color0;
	float3 color1;
	float4 outColor;
	float4 textureSampleColor;
	constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
	
	//テクスチャサンプリング色
	textureSampleColor = texture.sample(textureSampler, vert.textureCoords);
	
	//マテリアル拡散光色を頂点色で上書きする
	material = uniforms.material;
	material.diffuseColor = vert.diffuseColor.xyz;
	
	if (uniforms.enableLights == 0) {
		//ライト無効
		outColor = vert.diffuseColor * textureSampleColor;
	}
	else {
		//ライト有効
		color0 = dirLightColor(material, uniforms.light0, vert.normal.xyz, vert.eye.xyz);
		color1 = dirLightColor(material, uniforms.light1, vert.normal.xyz, vert.eye.xyz);
		outColor = float4(color0 + color1, vert.diffuseColor.a) * textureSampleColor;
	}
	
	return outColor;
}

//******************************************************************************
// ディレクショナルライト色計算
//******************************************************************************
float3 dirLightColor(
		 OGLMaterial material,	//マテリアル
		 OGLLight light,		//ライト：フラグメントから光源への方向ベクトルを持つ
		 float3 normal,			//法線ベクトル
		 float3 eyeDir			//フラグメントから視点へのベクトル
	)
{
	float3 n_lightDir;
	float3 n_normal;
	float3 n_eyeDir;
	float3 reflectDir;
	float diff;
	float spec;
	float3 ambientTerm;
	float3 diffuseTerm;
	float3 specularTerm;
	float3 outColor;
	
	//ライト無効の場合
	if (light.enable == 0) {
		outColor = float3(0, 0, 0);
		return outColor;
	}
	
	//正規化
	n_lightDir = normalize(light.direction);
	n_normal = normalize(normal);
	n_eyeDir = normalize(eyeDir);
	
	//係数
	diff = saturate(dot(n_normal, n_lightDir));
	reflectDir = reflect(-n_lightDir, n_normal);
	spec = pow(saturate(dot(n_eyeDir, reflectDir)), material.specularPower);
	
	//MIDITrail Ver.1.x.x（OpenGL利用時）の出力色に合わせるため 1.2倍する
	//この差異が発生する理由は不明
	diff = diff * 1.2f;
	
	//環境光／拡散光／鏡面反射光
	ambientTerm = light.ambientColor * material.ambientColor;
	diffuseTerm = light.diffuseColor * diff * material.diffuseColor;
	specularTerm = light.specularColor * spec * material.specularColor;
	
	//出力色
	outColor = float3(diffuseTerm + ambientTerm + specularTerm);
	
	return outColor;
}

