#version 430

#extension GL_NV_gpu_shader5 : enable

#define LIGHT_PROPERTIES_BINDING 1
#ifndef MATERIAL_PROPERTIES_BINDING
#define MATERIAL_PROPERTIES_BINDING 2
#endif

#include <shaders/materials/commons.glsl>
#include <shaders/materials/commons_rand.glsl>
#include <shaders/commons_hlsl.glsl>
#include <shaders/particles/particles_commons.glsl>

uniform sampler2DArray s_BlueNoise;
#include <shaders/deferred/lighting_support.glsl>

layout(std140, row_major) uniform TransformParamsBuffer{
	EntityTransformParams transform_params;
};

out Vertex
{
	vec3 vCoords;
	f16vec3 vNorm;
	f16vec3 vWorldNorm;
	vec3 vLocalPos;
	vec3 vWorldPos;
	f16vec4 vColor;
	f16vec2 vUV0;
} vtx_output;

uniform int materialIndex;
uniform float instancing_scale;
uniform int instancing_clones;					// how many times base mesh is clones
uniform int instancing_base_verts_num;			// how many verts total it has

void main() {

	MaterialPropertiesGPU material = materials.material_properties[materialIndex];

	// fetch input data. when generating quad flip faces for the shadowmap as we are rendering backfaces
	int vidx = gl_InstanceID * instancing_clones + gl_VertexID / instancing_base_verts_num;
	int quad_vidx = gl_VertexID & 3;

	vec3 quad_coords;

	// indices are like this
	// 0 --- 1
	//
	//
	// 3 --- 2
	//

	quad_coords.x = (quad_vidx == 1 || quad_vidx == 2) ? -0.5 : +0.5;
	quad_coords.y = (quad_vidx == 0 || quad_vidx == 1) ? +0.5 : -0.5;
	quad_coords.z = 0.0;

	vtx_output.vUV0 = f16vec2(quad_coords + 0.5); // hijack

	// generate quad coords

	//int vidx = gl_InstanceID;
	vec3 vInstPosition = prt_get_position(vidx);
	vec4 vInstColor = prt_get_color(vidx);
	ParticleState vState = prt_get_state(vidx);

	float quad_scale = instancing_scale;

	// transform by transpose of modelview
	quad_coords = vec3(
		dot(quad_coords, transform_params.mModelView[0].xyz),
		dot(quad_coords, transform_params.mModelView[1].xyz),
		dot(quad_coords, transform_params.mModelView[2].xyz));

	vInstPosition = vInstPosition + quad_coords *quad_scale;
	vec3 pos = vInstPosition;

	if (!prt_is_alive(vidx))
	{
		pos = vec3(0.0);
	}
	vtx_output.vLocalPos = pos;

	vec3 vPos1 = pos;
	vec3 vPos = vector_transform_by_mat43(vPos1, transform_params.mModelView);
	vtx_output.vWorldPos = vector_transform_by_mat43(vPos1, transform_params.mModel);

	vtx_output.vNorm = f16vec3(0.0, 0.0, 1.0);
	vtx_output.vWorldNorm = f16vec3(0.0, 0.0, 1.0);

	gl_Position = transform_params.mProjection * vec4(vPos, 1.0);

	// lighting. TODO: make it configurable

	{
		vec3 light_color = vec3(0.0);
		for (int light_idx = 0; light_idx < g_lights_num; light_idx++)
		{
			LightProperties light = lights.light_properties[light_idx];
			f16vec4 projector_color = f16vec4(0.0);
			vec3 world = vtx_output.vWorldPos;// -transform_params.vCameraPosition.xyz;

			float shadow = 0.0;
			{
				bool calculate_shadows = (light.type & LightType_Shadowcasting) != 0;
				if ((light.type & (LightType_Spot | LightType_Directional)) != 0 && calculate_shadows)
				{
					float penumbra_scaling_factor = 0.5;

					vec4 vShadowCoords = light.mat_shadow_mvp[0] * vec4(world.xyz, 1.0);

					float in_frustum = 0.0;
					shadow = sampleShadowPCF(
						LightShadowmapCmpSamplers[light.shadowmap_sampler0],
						vShadowCoords,
						in_frustum,
						3,
						0.0001);
				}
			}

			shadow = 1.0 - shadow;

			if ((light.type & (LightType_Spot | LightType_Attenuating)) == (LightType_Spot | LightType_Attenuating))
			{
				float attenuation = light_calculate_spot_attenuation(light, world.xyz);
				shadow *= attenuation;
			}

			light_color.rgb += light.diffuse.rgb * sqrt(light.intensity) * shadow;
		}

		vInstColor.rgb *= light_color;
		vInstColor.rgb *= material.diffuse.rgb;
		vInstColor.rgb += material.emissive.rgb * material.emissive_factor;

		float life_time_factor = min(1.0, vState.life_time / vState.life_span);
		//if (life_time_factor < 0.1)
		life_time_factor = 1.0 - sin(life_time_factor * M_PI * 0.5);
		vInstColor.rgb *= 1.0 - pow(life_time_factor, 4.0);
	}

	//vInstColor.r = 100.0;

	vtx_output.vCoords = vPos.xyz;
	vtx_output.vColor = f16vec4(vInstColor);
}