// in this context include refers to another shader file 
includes = ["core/stingray_renderer/shader_libraries/common.shader_source", 
			"core/stingray_renderer/shader_libraries/lighting_common.shader_source", 
			"core/stingray_renderer/shader_libraries/particle_lighting_common.shader_source", 
			"core/stingray_renderer/shader_libraries/shadow_map_common.shader_source" ]

sampler_states = {
	shadow_map = {
		inherits = "clamp_point"
		states = {
			defined_D3D11 = {
				comparison_func = "less"
				filter = "comparison_min_mag_linear_mip_point"
			}
			defined_D3D12 = {
				comparison_func = "less"
				filter = "comparison_min_mag_linear_mip_point"
			}
			defined_GNM = {
				comparison_func = "less"
				filter = "min_mag_mip_linear"
			}
			defined_GL2 = {
				comparison_func = "less"
				filter = "min_mag_linear"
			}
		}
	}
}

render_states = { 		
	billboard_shadow_caster = {
		inherits = "shadow_caster"
		states = {
			ndefined_GBUFFER_PARTICLES = {
				z_write_enable = "false"
				
				write_mask0 = "red"
				cull_mode = "cull_none"
				blend_enable = "true"

				blend_op = "blend_op_add"
				src_blend = "blend_one"
				dest_blend = "blend_inv_src_color"
			}
		}
	}
	
	billboard_opacity = {
		inherits = "opacity"
		states = {
			blend_op = "blend_op_add"
			dest_blend = "blend_inv_src_alpha"
			src_blend = "blend_one"
			
			defined_WIREFRAME = {
				fill_mode = "fill_wireframe"
			}
			defined_SCREEN_SPACE = {
				cull_mode = "cull_none"
				z_enable = "false"
			}
			ndefined_SCREEN_SPACE = {
				z_enable = "true"
			}
			defined_LOW_RES = {
				write_mask0 = "red|green|blue|alpha"
				write_mask1 = "red|green"
			}
			ndefined_LOW_RES = {
				write_mask0 = "red|green|blue"
			}
		}
	}
}

hlsl_shaders = {
	billboard_depth_only = {
		includes = [ "common", "gbuffer_access" ]

		samplers = {
			defined_DIFFUSE_MAP = {
				diffuse_map = { sampler_states = "wrap_anisotropic_srgb" }
			}
		}

		code="""
			#if defined(DIFFUSE_MAP)
				#define UV0
				DECLARE_SAMPLER_2D(diffuse_map);
			#endif

			struct VS_INPUT {
				float4 position : POSITION;
				#ifdef GL2
					float2 corner_info : COLOR1;
				#else
					float2 corner_info : POSITION1;
				#endif
				float2 size : TEXCOORD7;
				#if defined(VERTEX_COLOR)
					float4 color : COLOR;
				#endif
				#if defined(ROTATION)
					float rotation : TEXCOORD1;
				#endif
				#if defined(UV_ANIMATION)
					float frame : TEXCOORD0;
				#endif
				#if defined(PIVOT)
					float2 pivot : TEXCOORD6;
				#endif
				#if defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED)
					float3 tangent : TANGENT;
					#if defined(EXTERNAL_ROTATION)
						float3 binormal : BINORMAL;
					#endif
				#endif
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				#if defined(UV0)
					float2 uv : TEXCOORD0;
				#endif
				#if defined(VERTEX_COLOR)
					float4 color : COLOR;
				#endif
				//float4 normal_depth : TEXCOORD1;
			};

			CBUFFER_START(c0)
				float4x4 view;
				float4x4 view_proj;
				#if defined(UV_ANIMATION)
					float2 animation_frame_size; // exports={ name="Frame Size" type="vector2" value=[0.1 0.1] min=[0 0] max=[1 1] step=[0.000244140625 0.000244140625]}
				#endif
			CBUFFER_END

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT i) {
				PS_INPUT o;

				#if defined(EXTERNAL_ROTATION)
					float3 y = i.tangent;
					float3 x = i.binormal;
				#elif defined(TANGENT_LOCKED)
					float3 y = i.tangent;
					float3 x = normalize(cross(normalize(i.position.xyz - camera_pos), y));
				#else
					float3 x = view._m00_m10_m20;
					float3 y = view._m02_m12_m22;
				#endif

				#if defined(ROTATION)
					float c = cos(i.rotation);
					float s = sin(i.rotation);
					float3 x_axis = x * c + y * s;
					float3 y_axis = y * c - x * s;
				#else
					float3 x_axis = x;
					float3 y_axis = y;
				#endif

				#if defined(PIVOT)
					float2 ci = i.corner_info;
					float2 corner = ci * ( (1-(ci*0.5+0.5)) * i.size + ci * (i.pivot * i.size) );
				#else
					float2 corner = i.corner_info * (i.size * 0.5);
				#endif

				float3 wp = i.position.xyz + (x_axis * corner.x + y_axis * corner.y);
				#if defined(NEEDS_WORLD_POS)
					o.wp = wp;
				#endif
				#if defined(HAS_CUSTOM_FOV)
					float4 p = mul(float4(wp, 1), camera_custom_fov_view_projection);
				#else
					float4 p = mul(float4(wp, 1), view_proj);
				#endif
				o.position = p;

				float3 n = lerp(normalize(wp - i.position.xyz), -view._m01_m11_m21, 0.5);
				n = mul(n, (half3x3)view);
				//o.normal_depth = float4(n, p.z);

				#if defined(UV0)
					float2 uv = (i.corner_info * float2(1,-1) * 0.5 + 0.5);
					#if defined(UV_ANIMATION)
						uv *= animation_frame_size;
						float n_frames = 1.f / animation_frame_size.x;
						int frame_x = fmod(i.frame, n_frames);
						int frame_y = i.frame / n_frames;
						float2 offset = float2(frame_x * animation_frame_size.x, frame_y * animation_frame_size.y);
						uv += offset;
					#endif
					o.uv = uv;
				#endif

				#if defined(VERTEX_COLOR)
					o.color = decode_vertex_color(i.color);
				#endif

				return o;
			}

			float4 fast_gamma_to_linear(float4 c) {
				return c * c;
			}

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT i) : SV_TARGET0 {
				#if defined(DIFFUSE_MAP)
					float4 c = TEX2D(diffuse_map, i.uv);
				#else
					float4 c = float4(1,1,1,1);
				#endif

				#if defined(VERTEX_COLOR)
					float4 vc = fast_gamma_to_linear(i.color);
				#else
					float4 vc = float4(1,1,1,1);
				#endif

				#if defined(GBUFFER_PARTICLES)
					#if defined(ONE_BIT_ALPHA)
						float op = c.a;
						#if defined(DISSOLVE_USING_VERTEX_ALPHA)
							one_bit_alpha_mask(op, 1-vc.a);
						#else
							one_bit_alpha_mask(op, ONE_BIT_ALPHA_REF);
						#endif
					#endif
					return float4(1,1,1,1);
				#else
					//float d = i.normal_depth.w - normalize(i.normal_depth.xyz) * 0.01;
					//return float4(d,d,d,c.a);
					c *= vc;
					return float4(c.aaaa);
				#endif
			}
		"""
	}

	particle_lighting = {
		vp_code = { ref = "code" }
		fp_code = { ref = "code" }

		code="""
			#if !defined(BLEND_ADDITIVE) && !defined(GBUFFER_PARTICLES) && !defined(DISTORTION) && !defined(DISABLE_LIGHTING)
				#define PARTICLE_LIGHTING
				#define VS_FOG  
				
				#if defined(D3D11) || defined(D3D12) || defined(GNM)
					#define BACK_LIGHTING
				#endif

				#if defined(LOCAL_LIGHTS)
					#define CALCULATE_LIGHTING
				#endif
			#else
				#if defined(TESSELLATION)
					#undef TESSELLATION
				#endif
			#endif

			#if defined(DIFFUSE_MAP) || defined(NORMAL_MAP) || defined(MATERIAL_MAP) || defined(DISTORTION)
				#define UV0
			#endif

			#if (defined(USE_DEPTH_RT) && defined(GBUFFER_PARTICLES)) || !defined(GBUFFER_PARTICLES) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED))
				#define NEEDS_LINEAR_DEPTH
			#endif

			#if defined(DIFFUSE_MAP)
				DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" }
			#endif

			#if defined(NORMAL_MAP)
				DECLARE_SAMPLER_2D(normal_map); // exports={ name="Normal Map" type="resource" }
			#endif

			#if defined(MATERIAL_MAP)
				DECLARE_SAMPLER_2D(material_map); // exports={ name="Glossiness/Specular/Mask Map" type="resource" }
			#endif

			#if defined(DISTORTION)
				DECLARE_SAMPLER_2D(normal_map); // exports={ name="Distortion Normal Map" type="resource" }
				DECLARE_SAMPLER_2D(hdr0);
			#endif

			#ifndef GBUFFER_PARTICLES
				#ifdef LOW_RES
					Texture2D<float4> linear_depth_div4;
				#else
					Texture2D<float4> linear_depth;
					#if defined(LOW_RES_ENABLED)
						DECLARE_SAMPLER_2D(hdr_transparent_div4);
						DECLARE_SAMPLER_2D(hdr_linear_depth_div4);
					#endif
				#endif
			#endif
		"""
	}

	billboard = {
		includes = [ "common", "gbuffer_access", "particle_lighting", "taa_offsets", "fog", "brdf", "shadow_bias", "shadow_map_filtering", "lighting", "clustered_shading", "radiosity_normal_mapping", "particle_debug" ]

		samplers = {
			defined_DIFFUSE_MAP = {
				diffuse_map = { sampler_states = "wrap_anisotropic_srgb" }
			}
			defined_NORMAL_MAP = {
				normal_map = { sampler_states = "wrap_anisotropic" }
			}
			defined_MATERIAL_MAP = {
				material_map = { sampler_states = "wrap_anisotropic_srgb" }
			}
			defined_DISTORTION = {
				normal_map = { sampler_states = "wrap_anisotropic" }
				hdr0 = { sampler_states = "clamp_linear" }
			}

			global_diffuse_map = { sampler_states = "clamp_linear"}
			
			ndefined_GBUFFER_PARTICLES = {
				ndefined_LOW_RES = {
					defined_LOW_RES_ENABLED = {
						hdr_transparent_div4 = { sampler_states = "clamp_linear" }
						hdr_linear_depth_div4 = { sampler_states = "clamp_linear" }
					}
				}
				
				defined_SHADOW_RECEIVING = {
					local_lights_shadow_atlas = { sampler_states = "shadow_map" }
					sun_shadow_map = { sampler_states = "shadow_map" }
					static_sun_shadow_map = { sampler_states = "shadow_map" }
				}
			}
		}

		stage_conditions = {
			hull = "!defined(BLEND_ADDITIVE) && !defined(GBUFFER_PARTICLES) && !defined(DISTORTION) && !defined(DISABLE_LIGHTING) && defined(TESSELLATION)"
			domain = "!defined(BLEND_ADDITIVE) && !defined(GBUFFER_PARTICLES) && !defined(DISTORTION) && !defined(DISABLE_LIGHTING) && defined(TESSELLATION)"
		}

		code="""
			struct VS_INPUT {
				float4 position : POSITION;
				#ifdef GL2
					float2 corner_info : COLOR1;
				#else
					float2 corner_info : POSITION1;
				#endif
				float2 size : TEXCOORD7; // exports={ name="Size" type="vector2" value=[0.1 0.1] min=[0 0] max=[10 10] step=[0.01 0.01] }
				#if defined(VERTEX_COLOR)
					float4 color : COLOR;
				#endif
				#if defined(ROTATION)
					float rotation : TEXCOORD1; // exports = { name="Rotation" type="scalar" value=0 min=0 max=6.28319 step=0.0174533 }
				#endif
				#if defined(UV_SCALE)
					#if defined(UV_ANIMATION)
						float3 uv_data : TEXCOORD0; // exports = { name="UV Scale/Frame" type="vector3" value=[0 0 0] min=[0 0 0] max=[100 100 100] step=[0.1 0.1 1] }
						#define uv_frame uv_data.z
					#else
						float2 uv_data : TEXCOORD0;
					#endif
					#define uv_scale uv_data.xy
				#else
					#if defined(UV_ANIMATION)
						float uv_frame : TEXCOORD0; // exports = { name="UV Frame" type="scalar" value=0 min=0 max=100 step=1 }
					#endif
				#endif
				#if defined(PIVOT)
					float2 pivot : TEXCOORD6; // exports={ name="Pivot" type="vector2" value=[0.5 0.5] min=[0 0] max=[1 1] step=[0.001 0.001] }
				#endif
				#if defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED)
					float3 tangent : TANGENT; // exports={ name="Tangent" type="vector3" value=[0 0 1] min=[-1 -1 -1] max=[1 1 1] step=[0.001 0.001 0.001] }
					#if defined(EXTERNAL_ROTATION)
						float3 binormal : BINORMAL; // exports={ name="Binormal" type="vector3" value=[1 0 0] min=[-1 -1 -1] max=[1 1 1] step=[0.001 0.001 0.001] }
					#endif
				#endif
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;
				#if defined(UV0)
					float2 uv : TEXCOORD0;
				#endif
				#if defined(VERTEX_COLOR)
					float4 color : COLOR;
				#endif
				#if defined(NEEDS_LINEAR_DEPTH)
					float linear_depth : TEXCOORD1;
				#endif

				#if defined(NEEDS_WORLD_POS)
					float4 wp : TEXCOORD5;
				#endif

				#if defined(GBUFFER_PARTICLES)
					#if defined(NORMAL_MAP)
						float3 tsm0 : TEXCOORD2;
						float3 tsm1 : TEXCOORD3;
						float3 tsm2 : TEXCOORD4;
					#else
						float3 normal : TEXCOORD2;
					#endif
				#endif

				#if defined(PARTICLE_LIGHTING)
					float4 basis0 : TEXCOORD2;
					float4 basis1 : TEXCOORD3;
					float4 basis2 : TEXCOORD4;
					#if defined(BACK_LIGHTING)
						float3 back_lighting : TEXCOORD6;
					#endif
				#endif

				#if defined(VS_FOG)
					float4 fog_params : TEXCOORD7;
				#endif
				
			};

			CBUFFER_START(c_billboard)
				#if defined(SCREEN_SPACE)
					float4x4 proj;
					float4x4 view;
				#else
					float4x4 view;
					float4x4 view_proj;
				#endif
				#if defined(UV_ANIMATION)
					float2 animation_frame_size; // exports={ name="Frame Size" type="vector2" value=[0.1 0.1] min=[0 0] max=[1 1] step=[0.000244140625 0.000244140625]}
				#endif
				#if defined(SOFT_PARTICLES)
					float depth_fade_distance; // exports={ name="Depth Fade Distance" type="scalar" value=1 min=0.01 max=30 step=0.1 }
				#endif
				#if defined(GBUFFER_PARTICLES) && !defined(MATERIAL_MAP)
					float specular; // exports={ name="Specular Mask" type="scalar" value=0.8 min=0.0 max=1.0 step=0.001 }
					float glossiness; // exports={ name="Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 }
				#endif
				#if !defined(PARTICLE_LIGHTING) && !defined(GBUFFER_PARTICLES)
					float emissive_particle_intensity;
				#endif
				#if defined(DISTORTION)
					float2 distortion_strength; // exports={ name="Distortion Strength" type="vector2" value=[1.0 1.0] min=[ 1 1 ] max=[300 300] step=[1 1] }
				#endif
			CBUFFER_END

			#if defined(TESSELLATION)
				#define NEEDS_WORLD_POS
				#undef VS_FOG

				struct VS_OUTPUT_HS_INPUT {
					float4 wp : WORLDPOS;
					float3 normal : TEXCOORD2;

					#if defined(UV0)
						float2 uv : TEXCOORD0;
					#endif
					#if defined(VERTEX_COLOR)
						float4 color : COLOR;
					#endif
					#if defined(NEEDS_LINEAR_DEPTH)
						float linear_depth : TEXCOORD1;
					#endif
				};

				struct HS_CONSTANT_DATA_OUTPUT {
					float edges[3] : SV_TessFactor;
					float inside : SV_InsideTessFactor;
				};

				#define VS_OUTPUT VS_OUTPUT_HS_INPUT

				// hull shader is just a pipe through shader
				#define HS_CONTROL_POINT_OUTPUT VS_OUTPUT_HS_INPUT

				HS_CONSTANT_DATA_OUTPUT hs_constants(InputPatch<VS_OUTPUT_HS_INPUT, 3> p)
				{
					HS_CONSTANT_DATA_OUTPUT output = (HS_CONSTANT_DATA_OUTPUT)0;

					float3 v0 = p[0].wp.xyz;
					float3 v1 = p[1].wp.xyz;
					float3 v2 = p[2].wp.xyz;

					#ifdef LOW_RES
						float2 half_res = (back_buffer_size * 0.25) * 0.5;
					#else
						float2 half_res = back_buffer_size * 0.5;
					#endif
					const float eps = 0.0001;
					const float wanted_tri_size = 16;
					float4 clip_rect = float4(-half_res.x, -half_res.y, half_res.x, half_res.y);

					#if defined(SCREEN_SPACE)
						float4 p0 = float4(v0, 1);
						float4 p1 = float4(v1, 1);
						float4 p2 = float4(v2, 1);
					#else
						#if defined(HAS_CUSTOM_FOV)
							float4 p0 = mul(float4(v0, 1), camera_custom_fov_view_projection);
							float4 p1 = mul(float4(v1, 1), camera_custom_fov_view_projection);
							float4 p2 = mul(float4(v2, 1), camera_custom_fov_view_projection);
						#else
							float4 p0 = mul(float4(v0, 1), view_proj);
							float4 p1 = mul(float4(v1, 1), view_proj);
							float4 p2 = mul(float4(v2, 1), view_proj);
						#endif
					#endif
					p0.xy = (p0.xy / (p0.w+eps)) * half_res;
					p1.xy = (p1.xy / (p1.w+eps)) * half_res;
					p2.xy = (p2.xy / (p2.w+eps)) * half_res;

					float4 tessellation_factors = float4(0,0,0,0);

					bool near_rejected = all(float3(p0.z,p1.z,p2.z) < 0.0f);
					bool left_rejected = all(float3(p0.x,p1.x,p2.x) < clip_rect.x);
					bool right_rejected = all(float3(p0.x,p1.x,p2.x) > clip_rect.z);
					bool top_rejected = all(float3(p0.y,p1.y,p2.y) > clip_rect.w);
					bool bottom_rejected = all(float3(p0.y, p1.y, p2.y) < clip_rect.y);
					bool culled = (near_rejected || left_rejected || right_rejected || top_rejected || bottom_rejected);
					if (!culled) {
						float3 tf = float3(length(p2 - p1) / wanted_tri_size,
							length(p2 - p0) / wanted_tri_size,
							length(p1 - p0) / wanted_tri_size);

						tessellation_factors = float4(tf.xyz,max(tf.z, max(tf.x, tf.y)));
					}

					//float4 tessellation_factors = float4(1,1,1,1);

					output.edges[0] = tessellation_factors.x;
					output.edges[1] = tessellation_factors.y;
					output.edges[2] = tessellation_factors.z;
					output.inside = tessellation_factors.w;

					return output;
				}

				[domain("tri")]
				[partitioning("fractional_odd")]
				[outputtopology("triangle_cw")]
				[outputcontrolpoints(3)]
				[patchconstantfunc("hs_constants")]
				[maxtessfactor(14)]
				HS_CONTROL_POINT_OUTPUT hs_main(InputPatch<VS_OUTPUT_HS_INPUT, 3> inputPatch, uint cp_id : SV_OutputControlPointID)
				{
					HS_CONTROL_POINT_OUTPUT	o;

					o.wp =	inputPatch[cp_id].wp;
					o.normal = inputPatch[cp_id].normal.xyz;
					#if defined(UV0)
						o.uv = inputPatch[cp_id].uv;
					#endif
					#if defined(VERTEX_COLOR)
						o.color = inputPatch[cp_id].color;
					#endif

					#if defined(NEEDS_LINEAR_DEPTH)
						o.linear_depth = inputPatch[cp_id].linear_depth;
					#endif

					return o;
				}

				[domain("tri")]
				PS_INPUT ds_main( HS_CONSTANT_DATA_OUTPUT input, float3 barycentric_coordinates : SV_DomainLocation, const OutputPatch<HS_CONTROL_POINT_OUTPUT, 3> triangle_patch )
				{
					PS_INPUT o;

					float3 wp = barycentric_coordinates.x * triangle_patch[0].wp.xyz + barycentric_coordinates.y * triangle_patch[1].wp.xyz + barycentric_coordinates.z * triangle_patch[2].wp.xyz;
					#if defined(NEEDS_WORLD_POS)
						o.wp = float4(wp, 1.0f);
					#endif

					#if defined(SCREEN_SPACE)
						o.position = float4(wp.xyz, 1.0);
					#else
						#if defined(HAS_CUSTOM_FOV)
							float4 p = mul(float4(wp.xyz, 1), camera_custom_fov_view_projection);
						#else
							float4 p = mul(float4(wp.xyz, 1), view_proj);
						#endif

						float4 proj_pos = p / p.w;
						proj_pos.xy += get_vs_halton_offset(frame_number);
						o.position = proj_pos * p.w;
					#endif
					float3 normal = normalize(barycentric_coordinates.x * triangle_patch[0].normal + barycentric_coordinates.y * triangle_patch[1].normal + barycentric_coordinates.z * triangle_patch[2].normal);

					#if defined(PARTICLE_LIGHTING)
						float3 back_lighting;
						calc_basis_lighting(o.basis0, o.basis1, o.basis2, back_lighting, wp, normal, view, o.position);
						#if defined(BACK_LIGHTING)
							o.back_lighting = back_lighting;
						#endif
					#endif

					#if defined(UV0)
						float2 uv = barycentric_coordinates.x * triangle_patch[0].uv + barycentric_coordinates.y * triangle_patch[1].uv + barycentric_coordinates.z * triangle_patch[2].uv;
						o.uv = uv;
					#endif

					#if defined(VERTEX_COLOR)
						float4 color = barycentric_coordinates.x * triangle_patch[0].color + barycentric_coordinates.y * triangle_patch[1].color + barycentric_coordinates.z * triangle_patch[2].color;
						o.color = color;
					#endif

					#if defined(NEEDS_LINEAR_DEPTH)
						o.linear_depth = barycentric_coordinates.x * triangle_patch[0].linear_depth + barycentric_coordinates.y * triangle_patch[1].linear_depth + barycentric_coordinates.z * triangle_patch[2].linear_depth;
					#endif

					return o;
				}
			#else
				#define VS_OUTPUT PS_INPUT
			#endif


			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			VS_OUTPUT vs_main(VS_INPUT i) {
				VS_OUTPUT o;

				#if defined(EXTERNAL_ROTATION)
					float3 y = i.tangent;
					float3 x = i.binormal;
				#elif defined(TANGENT_LOCKED)
					float3 y = i.tangent;
					float3 x = normalize(cross(normalize(i.position.xyz - camera_pos), y));
				#elif defined(SCREEN_SPACE)
					float3 x = float3(1,0,0);
					float3 y = float3(0,1,0); //float3(0,0,1);
				#else
					float3 x = view._m00_m10_m20;
					float3 y = view._m02_m12_m22;
				#endif

				#if defined(ROTATION)
					float c = cos(i.rotation);
					float s = sin(i.rotation);
					float3 x_axis = x * c + y * s;
					float3 y_axis = y * c - x * s;
				#else
					float3 x_axis = x;
					float3 y_axis = y;
				#endif

				#if defined(PIVOT)
					float2 ci = i.corner_info;
					float2 corner = ci * ( (1-(ci*0.5+0.5)) * i.size + ci * (i.pivot * i.size) );
				#else
					float2 corner = i.corner_info * (i.size * 0.5);
				#endif

				#if defined(SCREEN_SPACE)
					float3 wp = i.position.xzy + (x_axis * corner.x + y_axis * corner.y) / float3(normalize(camera_unprojection.xz), 1);
				#else
					float3 wp = i.position.xyz + (x_axis * corner.x + y_axis * corner.y);
				#endif

				#if defined(NEEDS_WORLD_POS)
					o.wp = float4(wp, 1.0f);
				#endif

				#if defined(SCREEN_SPACE)
					float4 p = float4(wp, 1); //mul(float4(wp, 1), proj);
				#else
					#if defined(HAS_CUSTOM_FOV)
						float4 p = mul(float4(wp, 1), camera_custom_fov_view_projection);
					#else
						float4 p = mul(float4(wp, 1), view_proj);
					#endif
				#endif
				#if !defined(TESSELLATION)
					float4 proj_pos = p / p.w;
					proj_pos.xy += get_vs_halton_offset(frame_number);
					o.position = proj_pos * p.w;
				#endif

				#if defined(PARTICLE_LIGHTING)
					#if defined(SCREEN_SPACE)
						float3 n = view._m01_m11_m21;
					#else
						float3 n = normalize(lerp(wp - i.position.xyz, -view._m01_m11_m21, 0.5));
					#endif
					n = mul(n, (float3x3)view);

					#if defined(TESSELLATION)
						o.normal = n;
					#else
						float3 back_lighting;
						calc_basis_lighting(o.basis0, o.basis1, o.basis2, back_lighting, wp, n, view, o.position);
						#if defined(BACK_LIGHTING)
							o.back_lighting = back_lighting;
						#endif
					#endif
				#endif

				#if defined(UV0)
					float2 uv = (i.corner_info * float2(1,-1) * 0.5 + 0.5);
					#if defined(UV_ANIMATION)
						uv *= animation_frame_size;
						float n_frames = 1.f / animation_frame_size.x;
						int frame_x = fmod(i.uv_frame, n_frames);
						int frame_y = i.uv_frame / n_frames;
						float2 offset = float2(frame_x * animation_frame_size.x, frame_y * animation_frame_size.y);
						uv += offset;
					#endif

					#if defined(GL2)
						uv = float2(uv.x, 1-uv.y);
					#endif

					#if defined(UV_SCALE)
						uv *= i.uv_scale;
					#endif
					o.uv = uv;
				#endif

				#if defined(VERTEX_COLOR)
					o.color = decode_vertex_color(i.color);
				#endif

				#if defined(NEEDS_LINEAR_DEPTH) || defined(VS_FOG)
					float l_depth = linearize_depth(p.z / p.w);
				#endif
				
				#if defined(NEEDS_LINEAR_DEPTH)
					o.linear_depth = l_depth;
				#endif
				
				#if defined(VS_FOG)
					o.fog_params = calc_fog_vs(wp, l_depth);
				#endif

				#if defined(GBUFFER_PARTICLES)
					float3 n = cross(x_axis, y_axis);

					#if defined(NORMAL_MAP)
						float3 t = x_axis;
						float3 b = y_axis;
						o.tsm0 = float3(t.x, b.x, n.x);
						o.tsm1 = float3(t.y, b.y, n.y);
						o.tsm2 = float3(t.z, b.z, n.z);
					#else
						o.normal = n;
					#endif
				#endif

				return o;
			}

			// Note: Alpha channel stored as sqrt(opacity) to preserve precision
			float4 fast_gamma_to_linear(float4 c) {
				return c * c;
			}

			#if defined(GBUFFER_PARTICLES)
				//DECLARE_SAMPLER_CUBE(global_diffuse_map);

				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				GBUFFER_OUT ps_main(PS_INPUT input)	{
					GBUFFER_OUT o;

					#if defined(USE_DEPTH_RT)
						DEPTH(o) = gbuffer_encode_depth(input.linear_depth);
					#endif

					#if defined(VERTEX_COLOR)
						half4 vc = input.color;
						vc = fast_gamma_to_linear(vc);
					#endif

					half op = 1.f;
					#ifdef DIFFUSE_MAP
						half4 dtex = TEX2D(diffuse_map, input.uv);
						float3 albedo = dtex.rgb;
						op = dtex.a;
					#else
						float3 albedo = float3(0.6,0.6,0.6);
					#endif

					#ifdef MATERIAL_MAP
						float3 gsm = TEX2D(material_map, input.uv).rgb;
						half2 specular_glossiness = float2(gsm.g, gsm.r);
						#ifdef ONE_BIT_ALPHA_FROM_MATERIAL_B
							op = gsm.b;
						#endif
					#else
						half2 specular_glossiness = float2(specular, glossiness);
					#endif

					#if defined(ONE_BIT_ALPHA)
						#if defined(VERTEX_COLOR) && defined(DISSOLVE_USING_VERTEX_ALPHA)
							one_bit_alpha_mask(op, 1-vc.a);
						#else
							one_bit_alpha_mask(op, ONE_BIT_ALPHA_REF);
						#endif
					#endif

					#if defined(VERTEX_COLOR)
						albedo *= vc.rgb;
					#endif

					BASE_COLOR(o) = gbuffer_encode_base_color(albedo);

					float3 world_space_normal = float3(0,0,0);
					#ifdef NORMAL_MAP
						float3 tnormal = decode_normal_map(TEX2D(normal_map, input.uv));
						world_space_normal = rotate_vector3(tnormal, input.tsm0, input.tsm1, input.tsm2);
					#else
						world_space_normal = normalize(input.normal);
					#endif

					NORMAL(o) = gbuffer_encode_normal(world_space_normal);
					ROUGHNESS(o) = gbuffer_encode_roughness(1.f - specular_glossiness.y);
					METALLIC(o) = 0.f;
					VELOCITY(o) = encode_velocity(0.0);
					DENSITY(o) = 1.0;
					AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f);
					//AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(rgbm_decode(TEXCUBELOD(global_diffuse_map, world_space_normal, 0)));

					return o;
				}
			#elif defined(DISTORTION)
				#ifdef LOW_RES
					struct PS_OUTPUT {
						half4 color : SV_TARGET0;
						half4 depth : SV_TARGET1;
					};

					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					PS_OUTPUT ps_main(PS_INPUT input)
				#else
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
				#endif
				{
					#ifdef LOW_RES
						// TODO: correct sample position?
						half2 screen_uv = input.position.xy / (back_buffer_size * 0.25);
					#else
						half2 screen_uv = input.position.xy / back_buffer_size;
					#endif

					#if defined(LOW_RES)
						float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0)));
					#else
						float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0)));
					#endif

					half4 tnormal = TEX2D(normal_map, input.uv);
					half2 distortion = (tnormal.xy * 2.0 - 1.0) / (back_buffer_size * 0.25);

					half alpha = tnormal.a;
					#if defined(VERTEX_COLOR)
						half4 vcol = fast_gamma_to_linear(input.color);
						alpha *= vcol.a;
					#endif
					#if defined(SOFT_PARTICLES)
						alpha *= saturate(abs(d - input.linear_depth) / depth_fade_distance);
					#endif

					distortion_strength *= alpha;
					half4 color = half4(TEX2D(hdr0, screen_uv + (distortion * distortion_strength)).rgb, alpha);
					
					#if defined(VERTEX_COLOR)
						color.rgb *= lerp(float3(1,1,1), vcol.rgb, color.a);
					#endif

					#ifdef LOW_RES
						PS_OUTPUT o;
						o.color = half4(color.rgb * color.a, color.a);
						float alpha_depth = input.linear_depth * color.a;
						o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0, color.a);
						return o;
					#else
						#if defined(LOW_RES_ENABLED) && !defined(SCREEN_SPACE)
							// Fade out high resolution particle if the low resolution particles is infront of the high resolution particle
							float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg;
							float depth_mean = particle_depth_values.x;
							float depth_variance = particle_depth_values.y - depth_mean*depth_mean;
							float background_depth = input.linear_depth;
							
							float diff = max(background_depth - depth_mean, 0.0);
							float C = 1.38629436112; // 2 * ln(2)
							float T = exp2(-diff*diff / (C * max(depth_variance, 0.001)));
							float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T);
							color.a *= 1.0 - alpha_modifier;
						#endif
						#if defined(PARTICLE_DEBUG)
							return DISTORTION_PARTICLES_DEBUG_COLOR;
						#else
							return half4(color.rgb * color.a, color.a);
						#endif
					#endif
				}
			#elif defined(WIREFRAME)
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				float4 ps_main(PS_INPUT input) : SV_TARGET0 {
					return float4(1,0,0,1);
				}
			#else
				#ifdef LOW_RES
					struct PS_OUTPUT {
						half4 color : SV_TARGET0;
						half4 depth : SV_TARGET1;
					};
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					PS_OUTPUT ps_main(PS_INPUT input)
				#else
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
				#endif
				
				{
					#if defined(DIFFUSE_MAP)
						float4 c = TEX2D(diffuse_map, input.uv);
					#else
						float4 c = float4(1,1,1,1);
					#endif

					#if defined(SOFT_PARTICLES)
						#if defined(LOW_RES)
							float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0)));
						#else
							float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0)));
						#endif
					#endif

					#if defined(VERTEX_COLOR)
						float4 vc = input.color;
						vc = fast_gamma_to_linear(vc);
						c *= vc;
					#endif

					#if defined(SOFT_PARTICLES)
						c.a *= saturate(abs(d - input.linear_depth) / depth_fade_distance);
					#endif

					#if defined(PARTICLE_LIGHTING)
						float3 n = normalize(float3(input.basis0.w, input.basis1.w, input.basis2.w));
						c.rgb = calc_lighting(c, n, input.back_lighting, input.basis0.xyz, input.basis1.xyz, input.basis2.xyz);
						
						
						#if defined(VS_FOG)
							// apply vs calculated fog
							c = float4(lerp(c.rgb, input.fog_params.rgb, input.fog_params.a), c.a);
						#else
							c = apply_fog(c, input.wp.xyz, input.linear_depth);
						#endif
					#else
						c.rgb *= emissive_particle_intensity;
					#endif

					#if !defined(LOW_RES) && defined(LOW_RES_ENABLED) && !defined(SCREEN_SPACE)
						// Fade out high resolution particle if the low resolution particles is infront of the high resolution particle
						float2 screen_uv = input.position.xy / back_buffer_size;
						float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg;
						float depth_mean = particle_depth_values.x;
						float depth_variance = particle_depth_values.y - depth_mean*depth_mean;
						float background_depth = input.linear_depth;
						
						float diff = max(background_depth - depth_mean, 0.0);
						float C = 1.38629436112; // 2 * ln(2)
						float T = exp2(-diff*diff / (C * max(depth_variance, 0.001)));
						float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T);
						c.a *= 1.0 - alpha_modifier;
					#endif

					float alpha = c.a;
					c.rgb *= alpha;
					#ifdef BLEND_ADDITIVE 
						c.a = 0.0;
					#endif

					#ifdef LOW_RES
						PS_OUTPUT o;
						o.color = c;
						float alpha_depth = input.linear_depth * alpha;
						o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0.0, alpha);
						return o;
					#else
						#if defined(PARTICLE_DEBUG) && defined(PARTICLE_LIGHTING)
							return LIT_PARTICLES_DEBUG_COLOR;
						#elif defined(PARTICLE_DEBUG)
							return EMISSIVE_PARTICLES_DEBUG_COLOR;
						#else
							return c;
						#endif
					#endif
				}
			#endif
		"""
	}
}

shaders = {
	billboard = {
		// editor_advanced_mode = true

		editor_options = [
			{
				name="Quality"
				options = [
					{ name="High Resolution" define="HIGH_RESOLUTION" tool_tip="Particles are low resolution by default. If this flag is enabled the particle will be rendered in full resolution." }
				]
			}
			{
				name="Misc"
				options = [
					{ name="Custom FOV" define="HAS_CUSTOM_FOV" }
				]
			}
			{
				name="Turn-up Algorithms"
				options = [
					{ name="Screen space" define="SCREEN_SPACE" }
					{ name="Tangent Locked" define="TANGENT_LOCKED" }
					{ name="External Rotation" define="EXTERNAL_ROTATION" }
				]
			}
			{
				name="Vertex Modifiers"
				options = [
					{ name="Rotation" define="ROTATION" }
	 				{ name="Pivot" define="PIVOT" }
					{ name="UV Animation" define="UV_ANIMATION" }
					{ name="UV Scale" define="UV_SCALE" }
				]
			}
			{
				name="Pixel Modifiers"
				options = [
					{ name="Diffuse Map" define="DIFFUSE_MAP" }
					{ name="Vertex Color" define="VERTEX_COLOR" }
					{ name="Soft Particles" define="SOFT_PARTICLES" }
					{ name="Distortion Particles" define="DISTORTION" }
				]
			}
			{
				name="Lighting"
				options = [
					{ name="Disable" define="DISABLE_LIGHTING" }
					{ name="Disable Tessellation" define="DISABLE_TESSELLATION" }
					{ name="Disable Sun" define="DISABLE_SUN" }
					{ name="Disable Shadows" define="DISABLE_SHADOWS"  }
					{ name="Disable Local Lights" define="DISABLE_LOCAL_LIGHTS" }
				]
			}
			{
				name="GBuffer Particles"
				options = [
					{ name="Enable" define="LIT_PARTICLES" }
					{ name="Normal Map" define="NORMAL_MAP" }
					{ name="Material Map" define="MATERIAL_MAP" }
					{ name="One Bit Alpha" define="ONE_BIT_ALPHA" }
					{ name="Dissolve Using Vertex Alpha" define="DISSOLVE_USING_VERTEX_ALPHA" }
				]
			}
			{
				name="Blending"
				options = [
					{ name="Additive Blend" define="BLEND_ADDITIVE" }
				]
			}
		]

		contexts = {
			shadow_caster = {
				passes_sort_mode="immediate"
				passes = [{
					defined="BLEND_ADDITIVE"
					pass = [			
					]			
					fail = [{					
						defined="LIT_PARTICLES"
						pass = [		
							{ hlsl_shader="billboard_depth_only" defines=["GBUFFER_PARTICLES"] render_states="billboard_shadow_caster" }
						]
						fail = [
							// { hlsl_shader="billboard_depth_only" render_states="billboard_shadow_caster" }
						]
					}]
				}]
			}

			default = {
				passes = [{
					defined="LIT_PARTICLES"
					pass = [
						{ layer="gbuffer" hlsl_shader="billboard" defines=["GBUFFER_PARTICLES"] render_states="gbuffer_material" }
					]			
					fail = [
						{
							defined="PARTICLE_DEBUG"
							pass = [
								{ layer="hdr_transparent" hlsl_shader="billboard" render_states="billboard_opacity" }
							]			
							fail = [
								{
									defined="LOW_RES_ENABLED"
									pass = [
										{
											defined="HIGH_RESOLUTION"
											pass = [
												{
													defined="SCREEN_SPACE"
													pass = [
														{ layer="hdr_transparent_screen_space" hlsl_shader="billboard" render_states="billboard_opacity" }
													]						
													fail = [
														{ layer="hdr_transparent" hlsl_shader="billboard" render_states="billboard_opacity" }
													]
												}
											]						
											fail = [
												{
													defined="SCREEN_SPACE"
													pass = [
														{ layer="hdr_transparent_screen_space_low_res" hlsl_shader="billboard" defines=["LOW_RES"] render_states="billboard_opacity" }
													]						
													fail = [
														{ layer="hdr_transparent_low_res" hlsl_shader="billboard" defines=["LOW_RES"] render_states="billboard_opacity" }
													]
												}
											]
										}
									]						
									fail = [
										{
											defined="SCREEN_SPACE"
											pass = [
												{ layer="hdr_transparent_screen_space" hlsl_shader="billboard" render_states="billboard_opacity" }
											]						
											fail = [
												{ layer="hdr_transparent" hlsl_shader="billboard" render_states="billboard_opacity" }
											]
										}
									]
								}
							]
						}
					]
				}]
			}
		}

		compile = {
			shadow_caster = [
				{ if: "on_renderer(D3D11, D3D12, GNM)" }
			]
			default = [
				// TODO: tessellation is broken for ps4 atm
				// TODO: we only need to create these many permutations for particles that recieve lighting. => PARTICLE_LIGHTING
				{ if: "on_renderer(D3D11, D3D12, GNM, GL) && render_cap(development) && render_setting(particle_visualization) && !defined(LIT_PARTICLES)" defines=["PARTICLE_DEBUG"] }
				{ if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, low_res_transparency, particles_local_lighting, particles_receive_shadows, particles_tessellation)" defines=["LOW_RES_ENABLED", "LOCAL_LIGHTS" "SHADOW_RECEIVING" "TESSELLATION"] }
				{ if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, low_res_transparency, particles_local_lighting, particles_tessellation)" defines=["LOW_RES_ENABLED", "LOCAL_LIGHTS" "TESSELLATION"] }
				{ if: "on_renderer(D3D11, D3D12, GNM) 					&& !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, low_res_transparency, particles_local_lighting)" defines=["LOW_RES_ENABLED", "LOCAL_LIGHTS"] }
				{ if: "on_renderer(D3D11, D3D12, GNM) 					&& !defined(LIT_PARTICLES) && !defined(DISTORTION) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] }
				{ if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, particles_local_lighting, particles_receive_shadows, particles_tessellation)" defines=["LOCAL_LIGHTS" "SHADOW_RECEIVING" "TESSELLATION"] }
				{ if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, particles_local_lighting, particles_tessellation)" defines=["LOCAL_LIGHTS" "TESSELLATION"] }
				{ if: "on_renderer(D3D11, D3D12, GNM) 					&& !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, particles_local_lighting)" defines=["LOCAL_LIGHTS"] }
				{ if: "on_renderer(D3D11, D3D12, GNM, GL)" defines=[] }
			]
		}
	}
}
