
render_states = {
	// Stencil mask bit allocation:
	// |3221 1000|
	// 3 = Outline (1 = outline, 0 = no outline)
	// 2 = Decal projection mask (00 = not receiving, 01 = decal_group_1, 10 = decal_group_2, 11 = decal_group3)
	// 1 = Material mask (00 = default, 01 = SKIN)
	// 0 = Temporary / Scratch pad (used for Cascaded Shadow Map slice cut-out)


	default = {
		states = {
			defined_DOUBLE_SIDED = {
				cull_mode = "cull_none"
			}
			ndefined_DOUBLE_SIDED = {
				cull_mode = "cull_cw"
			}

			z_enable = "true"
			z_write_enable = "true"
			z_func = "less_equal"
			blend_enable = "false"
			write_mask0 = "red|green|blue|alpha"
			write_mask1 = "red|green|blue|alpha"
			write_mask2 = "red|green|blue|alpha"
			write_mask3 = "red|green|blue|alpha"

			stencil_enable = "false"
			stencil_func = "always"
			stencil_fail = "stencil_op_keep"
			stencil_mask = "0xff"
			stencil_pass = "stencil_op_keep"
			stencil_ref = "0x1"
			stencil_write_mask = "0xff"
			stencil_z_fail = "stencil_op_keep"

			stencil_func_back_side = "always"
			stencil_fail_back_side = "stencil_op_keep"
			stencil_pass_back_side = "stencil_op_keep"
			stencil_z_fail_back_side = "stencil_op_keep"

			defined_RENDERER_D3D11 = {
				depth_bias = "0"
				slope_scale_depth_bias = "0.0"

				independent_blend_enable = "false"
				sample_mask = "0xffffffff"
				nv_dbt_enable = "false"
				multisample_antialias = "true"

				write_mask4 = "red|green|blue|alpha"
				write_mask5 = "red|green|blue|alpha"
				write_mask6 = "red|green|blue|alpha"
				write_mask7 = "red|green|blue|alpha"

				defined_ALPHA_TO_COVERAGE = {
					alpha_to_coverage_enable = "true"
				}
				ndefined_ALPHA_TO_COVERAGE = {
					alpha_to_coverage_enable = "false"
				}
				srgb0 = "false"
			}
			defined_RENDERER_D3D12 = {
				depth_bias = "0"
				slope_scale_depth_bias = "0.0"

				independent_blend_enable = "false"
				sample_mask = "0xffffffff"
				nv_dbt_enable = "false"
				multisample_antialias = "true"

				write_mask4 = "red|green|blue|alpha"
				write_mask5 = "red|green|blue|alpha"
				write_mask6 = "red|green|blue|alpha"
				write_mask7 = "red|green|blue|alpha"

				defined_ALPHA_TO_COVERAGE = {
					alpha_to_coverage_enable = "true"
				}
				ndefined_ALPHA_TO_COVERAGE = {
					alpha_to_coverage_enable = "false"
				}
				srgb0 = "false"
			}

			defined_RENDERER_GL = {
				offset_factor = "0"
				offset_units = "0"
				depth_bias_enable = "false"
				srgb = "false"
				depth_bounds_test_enable = "false"
				depth_bounds_near = "0.0"
				depth_bounds_far = "0.0"
			}

			defined_RENDERER_GNM = {
				offset_clamp = "0"
 				offset_scale_front = "0"
 				offset_front = "0"
			}
		}
	}
	

	ambient = {
		inherits = "default"
	}

	gbuffer_ambient = {
		inherits = "default"
	}

	gbuffer_material = {
		inherits = "default"

		states = {
			// TODO: this does not work of some reason.
			//defined_RENDERER_D3D11 = {
			//defined_D3D11 = { // We need to enable this for the ps4
				stencil_enable = "true"
				stencil_mask = "0x78"
				stencil_write_mask = "0x78"

				stencil_func = "always"
				stencil_pass = "stencil_op_replace"
				stencil_fail = "stencil_op_keep"
				stencil_z_fail = "stencil_op_keep"

				stencil_func_back_side = "always"
				stencil_pass_back_side = "stencil_op_replace"
				stencil_fail_back_side = "stencil_op_keep"
				stencil_z_fail_back_side = "stencil_op_keep"

				// Observation: If we had a way to do sequenctial bit manipulation of state values this would be soo much cleaner, easier and more powerful!
				defined_DEFERRED_DECALS_GROUP_1 = {
					defined_SKIN = {
						stencil_ref = "0x28"
					}
					ndefined_SKIN = {
						stencil_ref = "0x20"
					}
				}
				ndefined_DEFERRED_DECALS_GROUP_1 = {
					defined_DEFERRED_DECALS_GROUP_2 = {
						defined_SKIN = {
							stencil_ref = "0x48"
						}
						ndefined_SKIN = {
							stencil_ref = "0x40"
						}
					}
					ndefined_DEFERRED_DECALS_GROUP_2 = {
						defined_DEFERRED_DECALS_GROUP_3 = {
							defined_SKIN = {
								stencil_ref = "0x68"
							}
							ndefined_SKIN = {
								stencil_ref = "0x60"
							}
						}
						ndefined_DEFERRED_DECALS_GROUP_3 = {
							defined_SKIN = {
								stencil_ref = "0x8"
							}
							ndefined_SKIN = {
								stencil_ref = "0x0"
							}
						}
					}
				}
			//}
		}
	}

	depth_only = {
		inherits = "default"
		states = {
			write_mask0 = "0x0"
			write_mask1 = "0x0"
			write_mask2 = "0x0"
			write_mask3 = "0x0"
		}
	}

	shadow_caster = {
		inherits = "depth_only"
		states = {
			defined_RENDERER_D3D11 = {
				depth_bias = "0xff"
				slope_scale_depth_bias = "2.5"
			}
			defined_RENDERER_D3D12 = {
				depth_bias = "0xff"
				slope_scale_depth_bias = "2.5"
			}
			defined_RENDERER_GL = {
				offset_factor = "1.1"
				offset_units = "4.0"
				depth_bias_enable = "true"
			}
			defined_RENDERER_GNM = {
				offset_clamp = "0.01"
				offset_scale_front = "16.0"
				offset_front = "0.0001"
				offset_z_format = "z_format_32"
			}
		}
	}

	opacity = {
		inherits = "default"
		states = {
			z_write_enable = "false"
			cull_mode = "cull_none"
			blend_enable = "true"
			blend_op = "blend_op_add"
			dest_blend = "blend_inv_src_alpha"
			src_blend = "blend_src_alpha"
		}
	}

	opacity_add = {
		inherits = "opacity"
		states = {
			dest_blend = "blend_one"
			src_blend = "blend_src_alpha"
		}
	}

	opacity_mul = {
		inherits = "opacity"
		states = {
			dest_blend = "blend_one"
			src_blend = "blend_dest_color"
		}
	}
}

sampler_states = {
	wrap_anisotropic = {
		states = {
			address_u = "address_wrap"
			address_v = "address_wrap"
			ndefined_RENDERER_GL = {
				filter = "anisotropic"
				max_anisotropy = "4"
			}
			defined_RENDERER_GL = {
				max_anisotropy = "2.0"
				filter = "min_mag_linear_mip_point"
			}

			srgb = "false"
		}
	}

	wrap_linear = {
		states = {
			address_u = "address_wrap"
			address_v = "address_wrap"
			defined_RENDERER_GL = {
				filter = "min_mag_linear_mip_point"
			}
			ndefined_RENDERER_GL = {
				filter = "min_mag_mip_linear"
			}
			srgb = "false"
		}
	}

	wrap_point = {
		states = {
			address_u = "address_wrap"
			address_v = "address_wrap"
			filter = "min_mag_mip_point"
			srgb = "false"
		}
	}

	wrap_anisotropic_srgb = {
		inherits = "wrap_anisotropic"
		states = {
			srgb = "true"
		}
	}

	wrap_linear_srgb = {
		inherits = "wrap_linear"
		states = {
			srgb = "true"
		}
	}

	wrap_point_srgb = {
		inherits="wrap_point"
		states = {
			srgb = "true"
		}
	}

	clamp = {
		states = {
			srgb = "false"
			defined_RENDERER_GL = {
				address_u = "address_clamp_to_edge"
				address_v = "address_clamp_to_edge"
				address_w = "address_clamp_to_edge"
			}
			defined_RENDERER_D3D11 = {
				address_u = "address_clamp"
				address_v = "address_clamp"
				address_w = "address_clamp"
			}
			defined_RENDERER_D3D12 = {
				address_u = "address_clamp"
				address_v = "address_clamp"
				address_w = "address_clamp"
			}
			defined_RENDERER_GNM = {
				address_u = "address_clamp"
				address_v = "address_clamp"
				address_w = "address_clamp"
			}
		}
	}

	clamp_point = {
		inherits="clamp"
		states = {
			filter = "min_mag_mip_point"
		}
	}

	clamp_linear = {
		inherits="clamp"
		states = {
			defined_RENDERER_GL = {
				filter = "min_mag_linear_mip_point"
			}
			ndefined_RENDERER_GL = {
				filter = "min_mag_mip_linear"
			}
		}
	}

	clamp_point_srgb = {
		inherits = "clamp_point"
		states = {
			srgb = "true"
		}
	}

	clamp_linear_srgb = {
		inherits = "clamp_linear"
		states = {
			srgb = "true"
		}
	}

	clamp_anisotropic_srgb = {
		inherits = "clamp"
		states = {
			srgb = "true"
			ndefined_RENDERER_GL = {
				filter = "anisotropic"
				max_anisotropy = "4"
			}
			defined_RENDERER_GL = {
				max_anisotropy = "2.0"
				filter = "min_mag_linear_mip_point"
			}
		}
	}

	clamp_anisotropic = {
		inherits = "clamp_anisotropic_srgb"
		states = {
			srgb = "false"
		}
	}
}

hlsl_shaders = {
	common = {
		// Compatibility stuff for old library compiler
		vp_code = { ref = "code" }
		ep_code = { ref = "code" }
		cp_code = { ref = "code" }
		gp_code = { ref = "code" }
		fp_code = { ref = "code" }

		code = """
			#if defined(RENDERER_GL)
				#define new_fixed(x) float((x))
				#define new_fixed2(x, y) vec2((x), (y))

				#define new_fixed3_xy(xy, z) vec3((xy), (z))
				#define new_fixed3(x, y, z) vec3((x), (y), (z))

				#define new_fixed4_xyz(xyz, w) vec4((xyz), (w))
				#define new_fixed4_xy(xy, z, w) vec4((xy), (z), (w))
				#define new_fixed4(x, y, z, w) vec4((x), (y), (z), (w))

				#define new_half(x) float((x))
				#define new_half2(x, y) vec2((x), (y))

				#define new_half3_xy(xy, z) vec3((xy), (z))
				#define new_half3(x, y, z) vec3((x), (y), (z))
				#define new_half3_xyz(xyz) vec3((xyz))

				#define new_half4_xyz(xyz, w) vec4((xyz), (w))
				#define new_half4_xy(xy, z, w) vec4((xy), (z), (w))
				#define new_half4(x, y, z, w) vec4((x), (y), (z), (w))

				#define new_float(x) float((x))
				#define new_float2(x, y) vec2((x), (y))

				#define new_float3_xy(xy, z) vec3((xy), (z))
				#define new_float3(x, y, z) vec3((x), (y), (z))

				#define new_float4_xyz(xyz, w) vec4((xyz), (w))
				#define new_float4_xy(xy, z, w) vec4((xy), (z), (w))
				#define new_float4(x, y, z, w) vec4((x), (y), (z), (w))
			#else
				#define new_fixed half
				#define new_fixed2 fixed2
				#define new_fixed3 fixed3
				#define new_fixed3_xy fixed3
				#define new_fixed4 fixed4
				#define new_fixed4_xy fixed4
				#define new_fixed4_xyz fixed4

				#define new_half half
				#define new_half2 half2
				#define new_half3 float3
				#define new_half3_xy float3
				#define new_half3_xyz float3
				#define new_half4 half4
				#define new_half4_xy half4
				#define new_half4_xyz half4

				#define new_float float
				#define new_float2 float2
				#define new_float3 float3
				#define new_float3_xy float3
				#define new_float4 float4
				#define new_float4_xy float4
				#define new_float4_xyz float4

				#define equal(a,b) (a)==(b)
				#define lessThan(a,b) (a)<(b)
				#define lessThanEqual(a,b) (a)<=(b)
				#define greaterThan(a,b) (a)>(b)
				#define greaterThanEqual(a,b) (a)>=(b)
				#define notEqual(a,b) (a)!=(b)
			#endif

			#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12)
				#define CBUFFER_START(name) cbuffer name {
				#define CBUFFER_END };
				#define DX10_STYLE_SAMPLERS
				#define NO_INTERPOLATION nointerpolation

			#elif defined(RENDERER_GNM)
				#pragma warning(disable: 5609) // On ps4, halfs are treated as floats. Let's disable the warning.
				#pragma warning(disable: 5203) // Todo: Remove this warning when GAME-6418 is closed.
		
				#pragma warning(disable: 5202)
				#pragma warning(disable: 5206)
				#pragma warning(disable: 6002)

				#define CBUFFER_START(name) ConstantBuffer name {
				#define CBUFFER_END };
				#define SV_TARGET0 S_TARGET_OUTPUT0
				#define SV_TARGET1 S_TARGET_OUTPUT1
				#define SV_TARGET2 S_TARGET_OUTPUT2
				#define SV_TARGET3 S_TARGET_OUTPUT3
				#define SV_POSITION S_POSITION
				#define SV_DEPTH S_DEPTH_OUTPUT

				#define Buffer DataBuffer
				#define SV_InstanceId S_INSTANCE_ID

				#define reversebits ReverseBits
				#define DX10_STYLE_SAMPLERS

				#define SV_TessFactor S_EDGE_TESS_FACTOR
				#define SV_InsideTessFactor S_INSIDE_TESS_FACTOR
				#define SV_OutputControlPointID S_OUTPUT_CONTROL_POINT_ID
				#define SV_DomainLocation S_DOMAIN_LOCATION
				#define domain DOMAIN_PATCH_TYPE
				#define partitioning PARTITIONING_TYPE
				#define outputtopology OUTPUT_TOPOLOGY_TYPE
				#define outputcontrolpoints OUTPUT_CONTROL_POINTS
				#define patchconstantfunc PATCH_CONSTANT_FUNC
				#define maxtessfactor MAX_TESS_FACTOR
				#if defined(STAGE_VERTEX)
					#define WORLDPOS S_POSITION
				#else
					#define WORLDPOS POSITION
				#endif
				#define SV_PrimitiveID S_PRIMITIVE_ID
				#define NO_INTERPOLATION nointerp
				
			#elif defined(RENDERER_GL)
				// Default to highp to imitate hlsl closer
				precision highp float;
				precision highp int;
				precision highp usampler2D;
				
				#define PRECISION precision
				#define LOWP lowp
				#define MEDIUMP mediump
				#define HIGHP highp

				#define inline
				#define static
				#define half mediump float
				#define half2 mediump vec2
				#define float3 mediump vec3
				#define half4 mediump vec4

				#define fixed lowp float
				#define fixed2 lowp vec2
				#define fixed3 lowp vec3
				#define fixed4 lowp vec4

				#define float2 vec2
				#define float3 vec3
				#define float4 vec4

				#define int2 ivec2
				#define int3 ivec3
				#define int4 ivec4

				#define uint2 uvec2
				#define uint3 uvec3
				#define uint4 uvec4

				#define float2x2 mat2
				#define float3x3 mat3
				#define float4x4 mat4

				#define mul(a, b) ((a) * (b))
				#define frac(a) fract(a)
				#define lerp(a, b, c) mix(a, b, c)
				#define modf mod

				// fmod() != mod() for negative numbers
				// % works on float for HLSL, but GLSL requires integral
				#define fmod(a, b) (a - b * trunc(a/b))

				#define ddx dFdx
				#define ddy dFdy
				#define rsqrt inversesqrt

				float saturate(float v) { return clamp(v, 0.f, 1.f); }
				vec2 saturate(vec2 v) { return clamp(v, vec2(0.f), vec2(1.f)); }
				vec3 saturate(vec3 v) { return clamp(v, vec3(0.f), vec3(1.f)); }
				vec4 saturate(vec4 v) { return clamp(v, vec4(0.f), vec4(1.f)); }

				#define CBUFFER_START(name)// uniform name {
				#define CBUFFER_END// };

				// Attribute mappings taken from the GL backend.
				//	{0, 15, -1, -1, -1, -1, -1, -1, -1, -1},    // POSITION
				//	{1, -1, -1, -1, -1, -1, -1, -1, -1, -1},	// NORMAL
				//	{2, -1, -1, -1, -1, -1, -1, -1, -1, -1},	// TANGENT
				//	{3, -1, -1, -1, -1, -1, -1, -1, -1, -1},	// BINORMAL
				//	{8, 9, 10, 11, 12, 13, 6, 7, 14, 15},		// TEXCOORD
				//	{4, 5, 6, 7, -1, -1, -1, -1, -1, -1},		// COLOR
				//	{15, -1, -1, -1, -1, -1, -1, -1, -1, -1},	// BLENDINDICES
				//	{14, -1, -1, -1, -1, -1, -1, -1, -1, -1},	// BLENDWEIGHTS

				#define POSITION0 0
				#define POSITION1 15

				#define NORMAL 1
				#define TANGENT 2
				#define BINORMAL 3

				#define TEXCOORD0 8
				#define TEXCOORD1 9
				#define TEXCOORD2 10
				#define TEXCOORD3 11
				#define TEXCOORD4 12
				#define TEXCOORD5 13
				#define TEXCOORD6 6
				#define TEXCOORD7 7
				#define TEXCOORD8 14
				#define TEXCOORD9 15

				#define COLOR0 4
				#define COLOR1 5
				#define COLOR2 6
				#define COLOR3 7

				#define BLENDINDICES 15
				#define BLENDWEIGHTS 14
			#endif

			#if defined(RENDERER_D3D12)
			
				#define RS1	"RootFlags (ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT), " \
					"CBV(b0, visibility = SHADER_VISIBILITY_VERTEX), " \
					"CBV(b1, visibility = SHADER_VISIBILITY_VERTEX), " \
					"CBV(b0, visibility = SHADER_VISIBILITY_PIXEL), " \
					"CBV(b1, visibility = SHADER_VISIBILITY_PIXEL), " \
					"DescriptorTable( CBV(b2, numDescriptors = 6), visibility = SHADER_VISIBILITY_VERTEX), " \
					"DescriptorTable( CBV(b2, numDescriptors = 6), visibility = SHADER_VISIBILITY_PIXEL), " \
					"DescriptorTable( SRV(t0, numDescriptors = 24), visibility = SHADER_VISIBILITY_VERTEX), " \
					"DescriptorTable( SRV(t0, numDescriptors = 24), visibility = SHADER_VISIBILITY_PIXEL), " \
					"DescriptorTable( Sampler(s0, numDescriptors = 24), visibility = SHADER_VISIBILITY_VERTEX), " \
					"DescriptorTable( Sampler(s0, numDescriptors = 24), visibility = SHADER_VISIBILITY_PIXEL) "
				
				
				#define ROOT_SIGNATURE_ATTRIBUTE(root_signature) [RootSignature(root_signature)]
				#define DEFAULT_ROOT_SIGNATURE_ATTRIBUTE ROOT_SIGNATURE_ATTRIBUTE(RS1)
			#else
				#define ROOT_SIGNATURE_ATTRIBUTE(root_signature)
				#define DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			#endif
			
			
			#if defined(RENDERER_GL)
				#define DECLARE_SAMPLER_2D(name) uniform sampler2D name
				#define Sampler2D sampler2D
				#define TEX2D texture
				#define TEX2DLOD textureLod

				#define DECLARE_SAMPLER_CUBE(name) uniform samplerCube name
				#define SamplerCube samplerCube
				#define TEXCUBE texture
				#define TEXCUBELOD textureLod

				#define DECLARE_SAMPLER_3D(name) uniform sampler3D name
				#define Sampler3D sampler3D
				#define TEX3D texture
				#define TEX3DLOD textureLod

				#define DECLARE_COMPARISON_SAMPLER_2D(name) uniform highp sampler2DShadow name
				#define ComparisonSampler2D highp sampler2DShadow
				#define TEX2DCMP texture
				#define TEX2DCMPLOD0 textureLod

				#define DECLARE_COMPARISON_SAMPLER_CUBE(name) uniform highp samplerCubeShadow name
				#define ComparisonSamplerCube highp samplerCubeShadow
				#define TEXCUBECMP texture
				#define TEXCUBECMPLOD0 textureLod
			#elif defined(DX10_STYLE_SAMPLERS)
				#define CONCAT_(prefix, name) prefix ## name
				#define CONCAT(prefix, name) CONCAT_(prefix, name)
				#define TEXTURE_NAME(name) CONCAT(TEXTURE_PREFIX, name)
				#define SAMPLER_NAME(name) CONCAT(SAMPLER_PREFIX, name)

				struct Sampler2D { Texture2D tex; SamplerState state; };

				#define TEX2D(s, uv) s.tex.Sample(s.state, uv)
				#if defined(GNM)
					#define TEX2DLOD(s, uv, lod) s.tex.SampleLOD(s.state, uv, lod)
				#else
					#define TEX2DLOD(s, uv, lod) s.tex.SampleLevel(s.state, uv, lod)
				#endif

				#define DECLARE_SAMPLER_2D(name)	\
						Texture2D TEXTURE_NAME(name); \
						SamplerState SAMPLER_NAME(name); \
						static Sampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) };
				
				#define DECLARE_SAMPLER_2D_SHARED(name, share_from)		\
						Texture2D TEXTURE_NAME(name);	\
						static Sampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) };
				
				
				
				struct SamplerCube { TextureCube tex; SamplerState state; };

				#define TEXCUBE(s, uv) s.tex.Sample(s.state, uv)
				#if defined(GNM)
					#define TEXCUBELOD(s, uv, lod) s.tex.SampleLOD(s.state, uv, lod)
				#else
					#define TEXCUBELOD(s, uv, lod) s.tex.SampleLevel(s.state, uv, lod)
				#endif

				#define DECLARE_SAMPLER_CUBE(name)	\
						TextureCube TEXTURE_NAME(name); \
						SamplerState SAMPLER_NAME(name); \
						static SamplerCube name = { TEXTURE_NAME(name), SAMPLER_NAME(name) };

				#define DECLARE_SAMPLER_CUBE_SHARED(name, share_from) \
						TextureCube TEXTURE_NAME(name);	\
						static SamplerCube name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) };
						
						
				struct Sampler3D { Texture3D tex; SamplerState state; };

				#define TEX3D(s, uv) s.tex.Sample(s.state, uv)
				#if defined(GNM)
					#define TEX3DLOD(s, uv, lod) s.tex.SampleLOD(s.state, uv, lod)
				#else
					#define TEX3DLOD(s, uv, lod) s.tex.SampleLevel(s.state, uv, lod)
				#endif

				#define DECLARE_SAMPLER_3D(name)	\
						Texture3D TEXTURE_NAME(name); \
						SamplerState SAMPLER_NAME(name); \
						static Sampler3D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) };

				#define DECLARE_SAMPLER_3D_SHARED(name, share_from)	\
						Texture3D TEXTURE_NAME(name); \
						static Sampler3D name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) };
						

				struct ComparisonSampler2D { Texture2D tex; SamplerComparisonState state; };

				#define TEX2DCMP(s, uv, cmp_value) s.tex.SampleCmp(s.state, uv, cmp_value)
				#if defined(GNM)
					#define TEX2DCMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLOD0(s.state, uv, cmp_value)
				#else
					#define TEX2DCMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLevelZero(s.state, uv, cmp_value)
				#endif

				#define DECLARE_COMPARISON_SAMPLER_2D(name)	\
						Texture2D TEXTURE_NAME(name); \
						SamplerComparisonState SAMPLER_NAME(name); \
						static ComparisonSampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) };

				#define DECLARE_COMPARISON_SAMPLER_2D_SHARED(name, share_from) \
						Texture2D TEXTURE_NAME(name);	\
						static ComparisonSampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) };

				struct ComparisonSamplerCube { TextureCube tex; SamplerComparisonState state; };

				#define TEXCUBECMP(s, uv, cmp_value) s.tex.SampleCmp(s.state, uv, cmp_value)
				#if defined(GNM)
					#define TEXCUBECMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLOD0(s.state, uv, cmp_value)
				#else
					#define TEXCUBECMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLevelZero(s.state, uv, cmp_value)
				#endif

				#define DECLARE_COMPARISON_SAMPLER_CUBE(name)	\
						TextureCube TEXTURE_NAME(name); \
						SamplerComparisonState SAMPLER_NAME(name); \
						static ComparisonSamplerCube name = { TEXTURE_NAME(name), SAMPLER_NAME(name) };						

				inline float4 Sample(Sampler2D s, float2 uv, int2 offset)
				{
					return s.tex.Sample(s.state, uv, offset);
				}

				inline float4 Sample(Sampler3D s, float3 uvw, int3 offset)
				{
					return s.tex.Sample(s.state, uvw, offset);
				}

				inline float4 Sample(Sampler2D s, float2 uv)
				{
					return s.tex.Sample(s.state, uv);
				}

				inline float4 Sample(Sampler3D s, float3 uvw)
				{
					return s.tex.Sample(s.state, uvw);
				}				
			#else
				#define DECLARE_SAMPLER_2D(name) sampler2D name;
				#define Sampler2D sampler2D
				#define TEX2D tex2D
				#define TEX2DLOD(s, uv, lod) tex2Dlod(s, float4(uv, 0, lod))

				#define DECLARE_SAMPLER_CUBE(name) samplerCUBE name;
				#define SamplerCube samplerCUBE
				#define TEXCUBELOD(s, uv, lod) texCUBElod(s, float4(uv, lod))
				#define TEXCUBE(s, uv) texCUBE(s, uv)

				#define DECLARE_SAMPLER_3D(name) sampler3D name;
				#define Sampler3D sampler3D
				#define TEX3D(s, uv) tex3D(s, uv)
				#define TEX3DLOD(s, uv, lod) tex3Dlod(s, float4(uv, lod))
			#endif

			#if defined(RENDERER_GL)
				#define UNIFORM uniform
			#else
				#define UNIFORM
			#endif

			#define MAX_RIMLIGHT_EXPONENT 8.0
			#define MAX_GLOSSINESS 500.0
			#define ONE_BIT_ALPHA_REF 0.5
			#define PI 3.14159265358979323846

			inline float3x3 to_mat3(float4x4 m)
			{
				#if defined(RENDERER_GL)
					return mat3(m);
				#else
					return (float3x3)m;
				#endif
			}

			#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) || (defined(RENDERER_GL) && defined(STAGE_FRAGMENT))
				inline void one_bit_alpha_mask(float alpha, float ref) {
					if (alpha < ref)
						discard;
				}
			#endif

			#if defined(RENDERER_GNM)
				inline bool front_facing(bool vface) {
					return vface;
				}
			#else
				inline bool front_facing(float vface) {
					return vface > 0.0;
				}
			#endif

			//remap color to RGBA using swizzling
			inline float4 decode_vertex_color(float4 col) {
				#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) || defined(RENDERER_GL)
					return col.bgra;
				#else
					return col;
				#endif
			}

			inline float3 decode_signed_normal(float3 v) {
				return 2.0*(v - 0.5);
			}

			inline float3 encode_signed_normal(float3 v) {
				return v*0.5 + 0.5;
			}

			inline float3 decode_normal_map(half4 v) {
				half4 temp = 2.0*(v - 0.5);
				return new_half3(temp.x, temp.y, sqrt(max(1.0 - dot(temp.xy, temp.xy), 0.0)));
			}

			inline half2 decode_normal_grad(half4 v) {
				half4 temp = 2.0*(v - 0.5);
				#if defined(RENDERER_GNM)
					return half2(temp.w, temp.y);
				#else
					return new_half2(temp.x, temp.y);
				#endif
			}

			inline half4 rgbm_encode(float3 color) {
				half4 rgbm;
				color = new_half3_xyz(sqrt(color));
				color *= new_half(1.0 / 6.0);
				rgbm.a = clamp(max(max(color.r, color.g ), color.b), 0.0, 1.0);
				rgbm.a = new_half(ceil(rgbm.a * 255.0) / 255.0);
				rgbm.rgb = color / rgbm.a;
				return rgbm;
			}

			inline float3 rgbm_decode(half4 rgbm) {
				float3 c = 6.0 * rgbm.rgb * rgbm.a;
				return c * c;				
			}

			inline void tspace_transform_transpose(out float3 tangent_out, out float3 binormal_out, out float3 normal_out, in float3 tangent, in float3 binormal, in float3 normal, float3x3 mat) {
				float3 t = normalize(mul(tangent, mat));
				float3 b = normalize(mul(binormal, mat));
				float3 n = normalize(mul(normal, mat));

				// find transpose of 3x3matrix
				tangent_out.x = t.x; tangent_out.y = b.x; tangent_out.z = n.x;
				binormal_out.x = t.y; binormal_out.y = b.y; binormal_out.z = n.y;
				normal_out.x = t.z; normal_out.y = b.z; normal_out.z = n.z;
			}

			inline void tspace_transform(out float3 tangent_out, out float3 binormal_out, out float3 normal_out, in float3 tangent, in float3 binormal, in float3 normal, float3x3 mat) {
				tangent_out = normalize(mul(tangent, mat));
				binormal_out = normalize(mul(binormal, mat));
				normal_out = normalize(mul(normal, mat));
			}

			inline void tspace_transpose(out float3 tsm0, out float3 tsm1, out float3 tsm2, in float3 t, in float3 b, in float3 n) {
				tsm0 = float3(t.x, b.x, n.x);
				tsm1 = float3(t.y, b.y, n.y);
				tsm2 = float3(t.z, b.z, n.z);
			}

			// half and float types are considered the same on gl and gnm platforms.
			#if !(defined(RENDERER_GL) || defined(RENDERER_GNM))
				inline half3 rotate_vector3(half3 v, half3 x, half3 y, half3 z) {
					return new_half3_xyz(normalize(half3(dot(v, x), dot(v, y), dot(v, z))));
				}
			#endif

			inline float3 rotate_vector3(float3 v, float3 x, float3 y, float3 z) {
				return normalize(new_float3(dot(v, x), dot(v, y), dot(v, z)));
			}

			inline float3 rotate_point3(float3 v, float3 x, float3 y, float3 z) {
				return new_float3(dot(v, x), dot(v, y), dot(v, z));
			}

			inline float3 fast_gamma_to_linear_rgb(float3 c) {
				return c * c;
			}

			inline float4 fast_gamma_to_linear_rgb(float4 c) {
				return new_float4_xyz(c.rgb * c.rgb, c.a);
			}

			inline float4 fast_gamma_to_linear_rgba(float4 c) {
				return c * c;
			}

			inline float3 fast_linear_to_gamma_rgb(float3 c) {
				return sqrt(c);
			}

			inline float4 fast_linear_to_gamma_rgb(float4 c) {
				return new_float4_xyz(sqrt(c.rgb), c.a);
			}

			inline float4 fast_linear_to_gamma_rgba(float4 c) {
				return sqrt(c);
			}

			CBUFFER_START(global_viewport)
				UNIFORM float3 camera_unprojection; // Deprecated uniform
				UNIFORM float3 camera_pos;
				UNIFORM float4x4 camera_view;
				UNIFORM float4x4 camera_projection;
				UNIFORM float4x4 camera_inv_view;
				UNIFORM float4x4 camera_inv_projection;
				UNIFORM float4x4 camera_world;
				UNIFORM float4x4 camera_last_world;
				UNIFORM float4x4 camera_last_view;
				UNIFORM float4x4 camera_last_view_projection;
				UNIFORM float4x4 camera_custom_fov_view_projection; 		// TODO: Add a define in engine to only set this if we need it
				UNIFORM float4x4 camera_custom_fov_last_view_projection;	// TODO: Add a define in engine to only set this if we need it
				UNIFORM float time;
				UNIFORM float delta_time;
				UNIFORM float frame_number;
				UNIFORM float2 back_buffer_size;
				UNIFORM float taa_enabled;
				UNIFORM float debug_rendering;
				UNIFORM float gamma;
				// Screen space coordinate calculation
				// X = (X + 1) * Viewport.Width * 0.5 + Viewport.TopLeftX
				// Y = (1 - Y) * Viewport.Height * 0.5 + Viewport.TopLeftY
				// Z = Viewport.MinDepth + Z * (Viewport.MaxDepth - Viewport.MinDepth)
				// X, Y clip space coordinate range [-1, 1] =>transforms to screen space coordinate [0, 1920] and [0, 1080].
				UNIFORM float4 viewport; // width, height, top.x, top.y
				#if defined(D3D11)
					float capture_cubemap;
				#endif
				//float g_particle_lighting;
				float sun_shadows;
				float skin_material_enabled;
			CBUFFER_END

			// will soon merge with global_viewport..
			CBUFFER_START(global_camera)
				UNIFORM float3 camera_near_far;
			CBUFFER_END
			
			CBUFFER_START(c_environment_settings)
				#if defined(HAS_SUN_DIRECTION)
					UNIFORM float3 sun_direction;
				#endif
				#if defined(HAS_SUN_COLOR)
					UNIFORM float3 sun_color;
				#endif
			CBUFFER_END
		"""
	}

	gbuffer_access = {
		// Compatibility stuff for old library compiler
		fp_code = { ref = "code" }
		vp_code = { ref = "code" }

		code =  """
			// Materials
			inline float2 encode_float_rg(float v) {
				float2 enc = new_float2(1.0, 255.0) * v;
				enc = frac(enc);
				enc -= enc.y * new_float2(1.0/255.0, 1.0/255.0);
				return enc;
			}

			inline float decode_float_rg(float2 rg) {
				return dot(rg, new_float2(1.0, 1.0/255.0));
			}

			inline float4 encode_float_rgba(float v) {
				float4 enc = new_float4(1.0, 255.0, 65025.0, 160581375.0) * v;
				enc = frac(enc);
				enc -= enc.yzww * new_float4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0);
				return enc;
			}

			inline float decode_float_rgba(float4 rgba) {
				return dot(rgba, new_float4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0));
			}

			#define DEFAULT_MATERIAL 0
			#define CLOTH_MATERIAL 1.0/255.0
			#define SKIN_MATERIAL 2.0/255.0
			#define COLORED_SPECULAR_MATERIAL 3.0/255.0

			#if defined(RENDERER_GL) && defined(STAGE_FRAGMENT)
				#define GBUFFER_OUTPUT \
					layout(location = 0) out mediump vec4 out_gbuffer0; \
					layout(location = 1) out mediump vec4 out_gbuffer1; \
					layout(location = 2) out mediump vec4 out_gbuffer2; \
					layout(location = 3) out mediump vec4 out_gbuffer3

				#define out_base_color out_gbuffer0.rgb
				#define out_metallic out_gbuffer0.a
				#define out_normal out_gbuffer1.rgb
				#define out_roughness out_gbuffer1.a
				#define out_velocity out_gbuffer2.ba
				#define out_ambient_diffuse_light out_gbuffer3
				#define out_ambient_occlusion out_gbuffer2.r
				#define out_density out_gbuffer2.g
			#elif !defined(RENDERER_GL)
				struct GBUFFER_OUT {
					half4 buffer0 : SV_TARGET0;
					half4 buffer1 : SV_TARGET1;
					half2 buffer2 : SV_TARGET2;
					// half4 buffer3 : SV_TARGET3; // Since we don't have baked lighting, we don't use this
					half2 buffer4 : SV_TARGET3;
				};
				#define BASE_COLOR(gbuffer) gbuffer.buffer0.rgb
				#define METALLIC(gbuffer) gbuffer.buffer0.a
				#define NORMAL(gbuffer) gbuffer.buffer1.rgb
				#define ROUGHNESS(gbuffer) gbuffer.buffer1.a
				// #define AMBIENT_DIFFUSE_LIGHT(gbuffer) gbuffer.buffer3.rgba
				#define VELOCITY(gbuffer) gbuffer.buffer4.rg
				#define AMBIENT_OCCLUSION(gbuffer) gbuffer.buffer2.r
				#define DENSITY(gbuffer) gbuffer.buffer2.g
			#endif
			
			#if defined(RENDERER_D3D12)
				inline float3 gbuffer_encode_base_color(float3 color) { return color; }
				inline float3 gbuffer_decode_base_color(half4 c) { return c.rgb; }
			#else
				inline float3 gbuffer_encode_base_color(float3 color) { return new_half3_xyz(sqrt(color.xyz)); }
				inline float3 gbuffer_decode_base_color(half4 c) { return c.rgb * c.rgb; }
			#endif
			
			inline float3 gbuffer_encode_normal_hiquality(float3 normal, Sampler2D nft) {
				normal = new_half3_xyz(normalize(normal));
				float3 abs_normal = abs(normal);
				half max_abs_normal = max(abs_normal.x, max(abs_normal.y, abs_normal.z));
				float2 tcoord = abs_normal.z < max_abs_normal ? (abs_normal.y < max_abs_normal ? abs_normal.yz : abs_normal.xz) : abs_normal.xy;
				tcoord = tcoord.x < tcoord.y ? tcoord.yx : tcoord.xy;
				tcoord.y /= tcoord.x;
				normal /= max_abs_normal;
				half fitting_scale = new_half(TEX2D(nft, tcoord).a);
				normal *= fitting_scale;
				return encode_signed_normal(normal);
			}

			inline float3 gbuffer_encode_normal(float3 normal) { return encode_signed_normal(normal);	}
			inline float3 gbuffer_decode_normal(half4 c) { return decode_signed_normal(c.rgb); }
			inline half gbuffer_encode_metallic_mask(half s) { return s; }
			inline half gbuffer_decode_metallic_mask(half4 c) {	return saturate(c.a); }
			inline half gbuffer_encode_roughness(half c) { return c; }
			inline half gbuffer_decode_roughness(half4 c) { return saturate(c.a); }
			inline half4 gbuffer_encode_ambient_diffuse_light(float3 col) { return rgbm_encode(new_half3_xyz(col)); }
			inline float3 gbuffer_decode_ambient_diffuse_light(half4 c) { return rgbm_decode(c); }
			inline half gbuffer_encode_ambient_occlusion(half c) { return c; }
			inline half gbuffer_decode_ambient_occlusion(half4 c) { return saturate(c.r); }
			inline half gbuffer_encode_density(half c) { return c; }
			inline half gbuffer_decode_density(half4 c) { return saturate(c.g); }

			inline float linearize_depth(float clip_depth) {
				return (camera_near_far.y*camera_near_far.x)/(camera_near_far.y - clip_depth*(camera_near_far.y-camera_near_far.x));
			}
			
			// SCALE_VELOCITY represents the maximum component size of a motion vector (in pixels).
			#define SCALE_VELOCITY 50.0
			#define HALF_ONE 127.0/255.0
			inline float2 encode_velocity(float2 v) {
				// On high-end platforms we don't encode velocity vectors (stored as 16F precision)
				#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
					return v;
				#else
					v  *= back_buffer_size/(SCALE_VELOCITY * 2.0);
					return clamp(v * 0.5 + HALF_ONE, 1.0/255.0, 1.0);
				#endif
			}

			inline float2 decode_velocity(float2 v) {
				// On high-end platforms we don't encode velocity vectors (stored as 16F precision)
				#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
					return v;
				#else
					v =  2.0 * (v - HALF_ONE);
					return v / (back_buffer_size / (SCALE_VELOCITY * 2.0));
				#endif
			}

			#if defined(RENDERER_GL)
				float gbuffer_decode_depth(uvec4 d) {
					return uintBitsToFloat(d.r);
				}
			#else
				inline float gbuffer_decode_depth(float4 c) {
					return c.r;
				}
			#endif

			inline float4 encode_world_pos(float4 p) {
				return new_float4_xyz(mul(mul(p,camera_inv_projection).xyz,to_mat3(camera_inv_view)),p.w);
			}

			// Function is deprecated. Use encode_world_pos(float4) instead.
			inline float4 encode_world_pos(float4 p, float3 unprojection) {
				return encode_world_pos(p);
			}

			inline float3 decode_world_pos(float4 p, float depth) {
				return (p.xyz / p.w) * depth + camera_pos;
			}
		"""
	}

	space_conversion = {

		// Compatibility stuff for old library compiler
		fp_code = { ref = "code" }
		vp_code = { ref = "code" }

		// Normalized Display Coordinates (variable prefix "ndc_")
		//               (1,1)
		//     .-----------.   (Z=1)
		//     |           |   /
		//     |     0     |  / 
		//     |           | /
		//     .-----------.(Z=0)
		//  (-1,-1)

		// Screen Space Coordinates (variable prefix "ss_")
		//               (1,0)
		//     0-----------.   (Z=1)
		//     |           |   /
		//     |           |  / 
		//     |           | /
		//     .-----------.(Z=0)
		//   (0,1)

		// View Space Coordinates (variable prefix "view_")
		//               (+,+)
		//     .-----------.    (Z=Far Plane)
		//     |           |   /
		//     |     0     |  / 
		//     |           | /
		//     .-----------.(Z=Near Plane)
		//   (-,-)

		// World Space Coordinates (variable prefix "world_")
		//
		//         (Z+) (Y+)
		//           |  /
		//           | /
		//           |/
		//    -------0------(X+)
		//          /
		//         /

		code =  """
			float3 ss_to_view(float3 ss_vector, bool is_point) {
				float x = ss_vector.x * 2 - 1;
				float y = (1 - ss_vector.y) * 2 - 1;
				float4 view_vector = mul(float4(x, y, ss_vector.z, is_point), camera_inv_projection);  
				view_vector.xyz /= view_vector.w;
				return view_vector.xyz;
			}

			float3 view_to_ss(float3 view_vector, bool is_point) {
				float4 ss_vector = mul(float4(view_vector, is_point), camera_projection);
				ss_vector.xyz /= ss_vector.w;
				ss_vector.xy = ss_vector.xy * float2(0.5, -0.5) + float2(0.5, 0.5);
				return ss_vector.xyz;
			}

			float3 world_to_view(float3 world_vector, bool is_point) {
				float4 view_vector = mul(float4(world_vector, is_point), camera_view);
				return view_vector.xyz;
			}

			float3 world_to_prev_view(float3 world_vector, bool is_point, bool lock_xyz = 0) {
				camera_last_view[3][0] *= !lock_xyz;
				camera_last_view[3][1] *= !lock_xyz;
				camera_last_view[3][2] *= !lock_xyz;
				float4 view_vector = mul(float4(world_vector, is_point), camera_last_view);
				return view_vector.xyz;
			}

		"""
	}

	skinning = {
		// This block uses the 'new' syntax as code sharing is very low between languages and it won't be included from any uber shaders.
		code = {
			glsl = """
				#if defined(SKINNED_1WEIGHT) || defined(SKINNED_2WEIGHTS) || defined(SKINNED_3WEIGHTS) || defined(SKINNED_4WEIGHTS)
					#define SKINNED
				#endif

				// TODO: Workaround long compile times, since all shaders are currently compiled with skinned option.
				#if defined(SKINNED) && defined(SKINNED_DISABLED)
					#undef SKINNED
				#endif

				#if defined(SKINNED)
					#define MAX_BONES 50
					CBUFFER_START(c_skin_matrices)
						UNIFORM highp mat4 bones[MAX_BONES];
						#if defined(MOTION_BLUR)
							UNIFORM highp mat4 last_bones[MAX_BONES];
						#endif
					CBUFFER_END

					#if defined(SKINNED_1WEIGHT)
						#define IndexType LOWP float
						#define WeightType LOWP float

						vec3 skin_point(vec4 p, IndexType bi, WeightType bw) {
							mat4 bone = bones[int(bi)];
							return (p * bone).xyz;
						}

						#if defined(MOTION_BLUR)
							vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) {
								mat4 bone = last_bones[int(bi)];
								return (p * bone).xyz;
							}
						#endif

						vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone = bones[int(bi)];
							return normalize(v * mat3(bone[0].xyz, bone[1].xyz, bone[2].xyz));
						}
						vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone = bones[int(bi)];
							return v * mat3(bone[0].xyz, bone[1].xyz, bone[2].xyz);
						}
					#elif defined(SKINNED_2WEIGHTS)
						#define IndexType LOWP vec2
						#define WeightType LOWP vec2

						vec3 skin_point(vec4 p, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							return (bw.x * (p * bone1) + bw.y * (p * bone2)).xyz;
						}
						#if defined(MOTION_BLUR)
							vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) {
								mat4 bone1 = last_bones[int(bi.x)];
								mat4 bone2 = last_bones[int(bi.y)];
								return (bw.x * (p * bone1) + bw.y * (p * bone2)).xyz;
							}
						#endif
						vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							return normalize( bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz))
											+ bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz)));
						}
						vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							return 	  bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz))
									+ bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz));
						}
					#elif defined(SKINNED_3WEIGHTS)
						#define IndexType LOWP vec3
						#define WeightType LOWP vec3

						vec3 skin_point(vec4 p, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							mat4 bone3 = bones[int(bi.z)];
							return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3)).xyz;
						}
						#if defined(MOTION_BLUR)
							vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) {
								mat4 bone1 = last_bones[int(bi.x)];
								mat4 bone2 = last_bones[int(bi.y)];
								mat4 bone3 = last_bones[int(bi.z)];
								return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3)).xyz;
							}
						#endif
						vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							mat4 bone3 = bones[int(bi.z)];
							return normalize( bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz))
											+ bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz))
											+ bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz)));
						}
						vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							mat4 bone3 = bones[int(bi.z)];
							return 	  bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz))
									+ bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz))
									+ bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz));
						}
					#elif defined(SKINNED_4WEIGHTS)
						#define IndexType LOWP vec4
						#define WeightType LOWP vec4

						vec3 skin_point(vec4 p, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							mat4 bone3 = bones[int(bi.z)];
							mat4 bone4 = bones[int(bi.w)];
							return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3) + bw.w * (p * bone4)).xyz;
						}
						#if defined(MOTION_BLUR)
							vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) {
								mat4 bone1 = last_bones[int(bi.x)];
								mat4 bone2 = last_bones[int(bi.y)];
								mat4 bone3 = last_bones[int(bi.z)];
								mat4 bone4 = last_bones[int(bi.w)];
								return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3) + bw.w * (p * bone4)).xyz;
							}
						#endif
						vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							mat4 bone3 = bones[int(bi.z)];
							mat4 bone4 = bones[int(bi.w)];
							return normalize( bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz))
											+ bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz))
											+ bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz))
											+ bw.w * (v * mat3(bone4[0].xyz, bone4[1].xyz, bone4[2].xyz)));
						}
						vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) {
							mat4 bone1 = bones[int(bi.x)];
							mat4 bone2 = bones[int(bi.y)];
							mat4 bone3 = bones[int(bi.z)];
							mat4 bone4 = bones[int(bi.w)];
							return 	  bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz))
									+ bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz))
									+ bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz))
									+ bw.z * (v * mat3(bone4[0].xyz, bone4[1].xyz, bone4[2].xyz));
						}
					#endif

					in IndexType blendindices;
					in WeightType blendweights;
				#endif
			"""

			hlsl = """
				#if defined(SKINNED_1WEIGHT) || defined(SKINNED_2WEIGHTS) || defined(SKINNED_3WEIGHTS) || defined(SKINNED_4WEIGHTS)
					#define SKINNED
				#endif

				// TODO: Workaround long compile times, since all shaders are currently compiled with skinned option.
				#if defined(SKINNED) && defined(SKINNED_DISABLED)
					#undef SKINNED
				#endif
	
				#if defined(SKINNED)
					CBUFFER_START(c_skin_matrices)
						#if defined(RENDERER_GL2)
							#define MAX_BONES 50
						#else
							#define MAX_BONES 100
						#endif
						float4x4 bones[MAX_BONES];

						#if defined(MOTION_BLUR)
							float4x4 last_bones[MAX_BONES];
						#endif
					CBUFFER_END

					#if defined(SKINNED_1WEIGHT)
						#define IndexType float1
						#define WeightType float1

						#if defined(GNM)
							// Avoids 'unreferenced variable' warning on ps4
							#define WEIGHT_PARAM_NAME 
						#else
							#define WEIGHT_PARAM_NAME bw
						#endif

						inline float3 skin_point(in float4 p, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) {
							return mul(p, (bones[(int)bi.x])).xyz;
						}
						#if defined(MOTION_BLUR)
							inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) {
								return mul(p, (last_bones[(int)bi.x])).xyz;
							}
						#endif
						inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) {
							return normalize(mul(v, (float3x3)(bones[(int)bi.x])));
						}
						inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) {
							return mul(v, (float3x3)(bones[(int)bi.x]));
						}
					#elif defined(SKINNED_2WEIGHTS)
						#define IndexType float2
						#define WeightType float2

						inline float3 skin_point(in float4 p, in IndexType bi, in WeightType bw) {
							return bw.x * mul(p, (bones[(int)bi.x])).xyz + bw.y * mul(p, (bones[(int)bi.y])).xyz;
						}
						#if defined(MOTION_BLUR)
							inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType bw) {
								return bw.x * mul(p, (last_bones[(int)bi.x])).xyz + bw.y * mul(p, (last_bones[(int)bi.y])).xyz;
							}
						#endif
						inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType bw) {
							return normalize(bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])));
						}
						inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType bw) {
							return bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])).xyz;
						}
					#elif defined(SKINNED_3WEIGHTS)
						#define IndexType float3
						#define WeightType float3

						inline float3 skin_point(in float4 p, in IndexType bi, in WeightType bw) {
							return bw.x * mul(p, (bones[(int)bi.x])).xyz + bw.y * mul(p, (bones[(int)bi.y])).xyz + bw.z * mul(p, (bones[(int)bi.z])).xyz;
						}
						#if defined(MOTION_BLUR)
							inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType bw) {
								return bw.x * mul(p, (last_bones[(int)bi.x])).xyz + bw.y * mul(p, (last_bones[(int)bi.y])).xyz + bw.z * mul(p, (last_bones[(int)bi.z])).xyz;
							}
						#endif
						inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType bw) {
							return normalize(bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z])));
						}
						inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType bw) {
							return bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z]));
						}
					#elif defined(SKINNED_4WEIGHTS)
						#define IndexType float4
						#define WeightType float4

						inline float3 skin_point(in float4 p, in IndexType bi, in WeightType bw) {
							return bw.x * mul(p, (bones[(int)bi.x])).xyz + bw.y * mul(p, (bones[(int)bi.y])).xyz + bw.z * mul(p, (bones[(int)bi.z])).xyz + bw.w * mul(p, (bones[(int)bi.w])).xyz;
						}
						#if defined(MOTION_BLUR)
							inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType bw) {
								return bw.x * mul(p, (last_bones[(int)bi.x])).xyz + bw.y * mul(p, (last_bones[(int)bi.y])).xyz + bw.z * mul(p, (last_bones[(int)bi.z])).xyz + bw.w * mul(p, (last_bones[(int)bi.w])).xyz;
							}
						#endif						
						inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType bw) {
							return normalize(bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z])) + bw.w * mul(v, (float3x3)(bones[(int)bi.w])));
						}
						inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType bw) {
							return bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z])) + bw.w * mul(v, (float3x3)(bones[(int)bi.w]));
						}
					#endif

					#define BLENDINDEXSEMANTIC BLENDINDICES

					#define SKIN_INPUT \
						IndexType blendindices : BLENDINDEXSEMANTIC; \
						WeightType blendweights : BLENDWEIGHTS;
				#else
					#define SKIN_INPUT
				#endif
			"""
		}
	}

	fog = {
		vp_code = { ref = "code" }
		fp_code = { ref = "code" }
		code="""
			CBUFFER_START(c_fog)
				UNIFORM float2 fog_depth_range;
				float2 height_fog_offset_falloff; // TODO: merge this param with fog_depth_range?
				float fog_type;
				UNIFORM float3 fog_color;
				#if !defined(HAS_SUN_DIRECTION)
					UNIFORM float3 sun_direction;
				#endif
				#if !defined(HAS_SUN_COLOR)
					UNIFORM float3 sun_color;
				#endif
				UNIFORM float3 fog_sun_blend;
				UNIFORM float fog_enabled;
			CBUFFER_END

			#define DEFAULT_EXP_HEIGHT 	0
			#define EXP_HEIGHT			1
			#define EXP					2
			#define LINEAR	 			3

			half4 apply_fog(half4 c, float3 wp, float d) {
				if (fog_enabled < 1.0)
					return c;

				float start = fog_depth_range.x;
				float end = fog_depth_range.y;
				float b = 1.f / (end-start);

				#if defined(RENDERER_GL)
					float3 view_dir = normalize(new_half3(camera_world[0].z, camera_world[1].z, camera_world[2].z) - wp);
					float camera_world_m32 = camera_world[2].z;
				#else
					float3 view_dir = normalize(camera_world._m30_m31_m32 - wp);
					float camera_world_m32 = camera_world._m32;
				#endif

				float dist = d - start;

				float a = 0.0;
				[branch]
				if (fog_type == DEFAULT_EXP_HEIGHT) {
					a = saturate(exp(-camera_world._m32 * b) * (1.0 - exp(-dist * -view_dir.z * b)) / -view_dir.z);
				} else if (fog_type == EXP_HEIGHT) {
					a = saturate(exp((height_fog_offset_falloff.x - wp.z)/height_fog_offset_falloff.y) * (1.0 - exp(-dist * b)));
				} else if (fog_type == EXP) {
					a = 1.0 - saturate(exp(-dist * b));
				} else {
					a = d > start ? saturate((d-start) * b) : 0;
				}

				half sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y);
				float3 fog_c = lerp(fog_color, fog_sun_blend.z * sun_color, sa);
				return new_half4_xyz(lerp(c.rgb, fog_c, a), c.a);
			}
			
			float4 calc_fog_vs(float3 wp, float d) {
				if (fog_enabled < 1.0)
					return float4(0,0,0,0);

				float start = fog_depth_range.x;
				float end = fog_depth_range.y;
				float b = 1.f / (end-start);

				#if defined(RENDERER_GL)
					float3 view_dir = normalize(new_half3(camera_world[0].z, camera_world[1].z, camera_world[2].z) - wp);
					float camera_world_m32 = camera_world[2].z;
				#else
					float3 view_dir = normalize(camera_world._m30_m31_m32 - wp);
					float camera_world_m32 = camera_world._m32;
				#endif

				float dist = d - start;

				float a = 0.0;
				[branch]
				if (fog_type == DEFAULT_EXP_HEIGHT) {
					a = saturate(exp(-camera_world._m32 * b) * (1.0 - exp(-dist * -view_dir.z * b)) / -view_dir.z);
				} else if (fog_type == EXP_HEIGHT) {
					a = saturate(exp((height_fog_offset_falloff.x - wp.z)/height_fog_offset_falloff.y) * (1.0 - exp(-dist * b)));
				} else if (fog_type == EXP) {
					a = 1.0 - saturate(exp(-dist * b));
				} else {
					a = d > start ? saturate((d-start) * b) : 0;
				}

				half sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y);
				float3 fog_c = lerp(fog_color, fog_sun_blend.z * sun_color, sa);
				return float4(fog_c, a);
				
			}			
			
			#undef DEFAULT_EXP_HEIGHT
			#undef EXP_HEIGHT
			#undef EXP		
			#undef LINEAR
		"""
	}
	
	/*shadow_bias = {
		code = """
			float apply_shadow_bias(float depth) {
				const float min_bias = 0.005;
				const float max_bias = 0.2;
				const float blend_distance = 50.0;
				depth -= lerp(min_bias, max_bias, saturate(depth / blend_distance));
				return depth;
			}
		"""
		fp_code = { ref = "code" }
	}
	*/
	color_management = {
		code="""
			#if defined(RENDERER_GL)
				#define splat3(c) vec3(c)
			#else
				#define splat3(c) c
			#endif

			static const float3 luminance_vector = float3(0.2127, 0.7152, 0.0721);
			static const half min_positive_f16 = 0.000061;

			float3 filmic_tone_mapping(float3 lin_x) {
				float3 x = max(splat3(0.f), lin_x - splat3(0.004f));
				return (x*(6.2f*x + splat3(0.5f)))/(x*(6.2f*x + splat3(1.7f)) + splat3(0.06f));
			}

			float3 safe_range_tone_map(float3 value) {
				float lum = max(dot(value.rgb, luminance_vector), min_positive_f16);
				value.rgb /= (1.0 + lum);

				return value;
			}

			float3 inv_safe_range_tone_map(float3 value) {
				float lum = dot(value.rgb, luminance_vector);
				value.rgb /= max((1.0 - lum), min_positive_f16);

				return value;
			}

			float3 safe_range_tone_map_offset(float3 value, float offset) {
				float lum = max(dot(value.rgb, luminance_vector), min_positive_f16);
				value.rgb /= (offset + lum);

				return value;
			}

			float3 inv_safe_range_tone_map_offset(float3 value, float offset) {
				float lum = dot(value.rgb, luminance_vector);
				value.rgb /= max((offset - lum), min_positive_f16);

				return value;
			}

			float luminance(float3 value) {
				return dot(value, luminance_vector);
			}

			#undef splat3
		"""
		fp_code = { ref = "code" }
	}

	billboard_transformation = {
		code = """
			// TODO: reuse code
			void get_billboard_data_from_position(in float3 world_pos, in float3 camera_pos, in float4x4 camera_view, inout float4 position, out float4 wp, out float3 normal, out float3 tangent, out float3 binormal) {
				wp = float4(world_pos, 1.0);
				#if defined(BILLBOARD_TANGENT_ALIGNED) || defined(BILLBOARD_CAMERA_ALIGNED)
					float3 eye_dir = normalize(camera_pos - wp.rgb);
				#endif
				#if defined(BILLBOARD_TANGENT_ALIGNED)
					#if defined(BILLBOARD_TANGENT_ALIGNED_X_LOCKED)
						float3 x_axis = float3(1, 0, 0);
						float3 y_axis = normalize(cross(eye_dir, x_axis));
					#elif defined(BILLBOARD_TANGENT_ALIGNED_Y_LOCKED)
						float3 x_axis = float3(0, 1, 0);
						float3 y_axis = normalize(cross(eye_dir, x_axis));
					#else
						float3 y_axis = float3(0, 0, 1);
						float3 x_axis = normalize(cross(y_axis, eye_dir));
					#endif
				#elif defined(BILLBOARD_CAMERA_ALIGNED)
					float3 y_axis = float3(0, 0, 1);
					float3 x_axis = normalize(cross(y_axis, eye_dir));
					y_axis = cross(eye_dir, x_axis);
				#else
					float3 x_axis = camera_view._m00_m10_m20;
					float3 y_axis = camera_view._m02_m12_m22;
				#endif
				position = float4(x_axis * position.x + y_axis * position.y, 1.0);
				wp.xyz += position.xyz;

				// Calculate normals and tangent info
				#if defined(BILLBOARD_TANGENT_ALIGNED)
					normal = cross(x_axis, y_axis);
				#elif defined(BILLBOARD_CAMERA_ALIGNED)
					normal = eye_dir; // TODO: is this same as -camera_view._m01_m11_m21?
				#else
					normal = -camera_view._m01_m11_m21;
				#endif
				#if defined(BILLBOARD_SPHERICAL_NORMAL)
					// TODO: re-calculate tangent and binormal
					normal = normalize(lerp(position, normal, 0.5));	
				#endif
				tangent = x_axis;
				binormal = y_axis;
			}

			void get_billboard_positions_from_position(in float3 world_pos, in float3 camera_pos, in float4x4 camera_view, inout float4 position, out float4 wp) {
				wp = float4(world_pos, 1.0);
				#if defined(BILLBOARD_TANGENT_ALIGNED) || defined(BILLBOARD_CAMERA_ALIGNED)
					float3 eye_dir = normalize(camera_pos - wp.rgb);
				#endif
				#if defined(BILLBOARD_TANGENT_ALIGNED)
					#if defined(BILLBOARD_TANGENT_ALIGNED_X_LOCKED)
						float3 x_axis = float3(1, 0, 0);
						float3 y_axis = normalize(cross(eye_dir, x_axis));
					#elif defined(BILLBOARD_TANGENT_ALIGNED_Y_LOCKED)
						float3 x_axis = float3(0, 1, 0);
						float3 y_axis = normalize(cross(eye_dir, x_axis));
					#else
						float3 y_axis = float3(0, 0, 1);
						float3 x_axis = normalize(cross(y_axis, eye_dir));
					#endif
				#elif defined(BILLBOARD_CAMERA_ALIGNED)
					float3 y_axis = float3(0, 0, 1);
					float3 x_axis = normalize(cross(y_axis, eye_dir));
					y_axis = cross(eye_dir, x_axis);
				#else
					float3 x_axis = camera_view._m00_m10_m20;
					float3 y_axis = camera_view._m02_m12_m22;
				#endif
				position = float4(x_axis * position.x + y_axis * position.y, 1.0);
				wp.xyz += position.xyz;
			}

			void get_billboard_data_from_direction(in float3 direction, inout float4 position, out float4 wp, out float3 normal, out float3 tangent, out float3 binormal) {
				wp = float4(-direction, 1.0);
				float3 y_axis = float3(0, 0, 1);
				float3 x_axis = normalize(cross(y_axis, direction));
				y_axis = cross(direction, x_axis);
				position = float4(x_axis * position.x + y_axis * position.y, 1.0);
				wp.xyz += position.xyz;

				// Calculate normals and tangent info
				normal = direction;
				#if defined(BILLBOARD_SPHERICAL_NORMAL)
					// TODO: re-calculate tangent and binormal
					normal = normalize(lerp(position, normal, 0.5));	
				#endif
				tangent = x_axis;
				binormal = y_axis;
			}

			void get_billboard_positions_from_direction(in float3 direction, inout float4 position, out float4 wp) {
				wp = float4(-direction, 1.0);
				float3 y_axis = float3(0, 0, 1);
				float3 x_axis = normalize(cross(y_axis, direction));
				y_axis = cross(direction, x_axis);
				position = float4(x_axis * position.x + y_axis * position.y, 1.0);
				wp.xyz += position.xyz;
			}
		"""
		fp_code = { ref = "code" }
	}

	taa_offsets = {
		code="""
			#if !defined(RENDERER_GL)
				CBUFFER_START(temporal_effects)
					UNIFORM float temporal_effects_enabled;
				CBUFFER_END

				// Halton offsets
				#define NUM_HALTON_OFFSETS 8
				#define BLACKMAN_HARRIS_SHARPNESS 2.0

				static const float2 halton_offsets[] = {
					float2((1.f/2.f  - 0.5f), (1.f/3.f - 0.5f)),
					float2((1.f/4.f  - 0.5f), (2.f/3.f - 0.5f)),
					float2((3.f/4.f  - 0.5f), (1.f/9.f - 0.5f)),
					float2((1.f/8.f  - 0.5f), (4.f/9.f - 0.5f)),
					float2((5.f/8.f  - 0.5f), (7.f/9.f - 0.5f)),
					float2((3.f/8.f  - 0.5f), (2.f/9.f - 0.5f)),
					float2((7.f/8.f  - 0.5f), (5.f/9.f - 0.5f)),
					float2((1.f/16.f - 0.5f), (8.f/9.f - 0.5f))
				};

				// Blackman-Harris pre calculated weights
				float gaussian_blackman_harris(float2 pos) {
					float x = pos.x * BLACKMAN_HARRIS_SHARPNESS;
					float y = pos.y * BLACKMAN_HARRIS_SHARPNESS;
					return exp(-2.29f * (x * x + y * y));
				}

				static const int2 neighbor_offsets_i[9] = {
					int2(-1, -1),
					int2( 0, -1),
					int2( 1, -1),
					int2(-1,  0),
					int2( 0,  0),
					int2( 1,  0),
					int2(-1,  1),
					int2( 0,  1),
					int2( 1,  1)
				};

				static const float2 neighbor_offsets[9] = {
					neighbor_offsets_i[0],
					neighbor_offsets_i[1],
					neighbor_offsets_i[2],
					neighbor_offsets_i[3],
					neighbor_offsets_i[4],
					neighbor_offsets_i[5],
					neighbor_offsets_i[6],
					neighbor_offsets_i[7],
					neighbor_offsets_i[8]
				};

				static const float blackman_harris_sum0 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[0]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[0]);

				static const float blackman_harris_weights0[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[0]) / blackman_harris_sum0,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[0]) / blackman_harris_sum0
				};

				static const float blackman_harris_sum1 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[1]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[1]);

				static const float blackman_harris_weights1[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[1]) / blackman_harris_sum1,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[1]) / blackman_harris_sum1
				};

				static const float blackman_harris_sum2 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[2]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[2]);

				static const float blackman_harris_weights2[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[2]) / blackman_harris_sum2,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[2]) / blackman_harris_sum2
				};

				static const float blackman_harris_sum3 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[3]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[3]);

				static const float blackman_harris_weights3[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[3]) / blackman_harris_sum3,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[3]) / blackman_harris_sum3
				};

				static const float blackman_harris_sum4 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[4]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[4]);

				static const float blackman_harris_weights4[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[4]) / blackman_harris_sum4,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[4]) / blackman_harris_sum4
				};

				static const float blackman_harris_sum5 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[5]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[5]);

				static const float blackman_harris_weights5[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[5]) / blackman_harris_sum5,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[5]) / blackman_harris_sum5
				};

				static const float blackman_harris_sum6 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[6]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[6]);

				static const float blackman_harris_weights6[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[6]) / blackman_harris_sum6,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[6]) / blackman_harris_sum6
				};

				static const float blackman_harris_sum7 = 
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[7]) + 
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[7]);

				static const float blackman_harris_weights7[9] = {
					gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[7]) / blackman_harris_sum7,
					gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[7]) / blackman_harris_sum7
				};

				static const float blackman_harris_weights[NUM_HALTON_OFFSETS][9] = {
					blackman_harris_weights0,
					blackman_harris_weights1,
					blackman_harris_weights2,
					blackman_harris_weights3,
					blackman_harris_weights4,
					blackman_harris_weights5,
					blackman_harris_weights6,
					blackman_harris_weights7
				};


				float2 get_vs_halton_offset(uint frame_number)
				{
					// Multiply the halton offset by 2 since we are adding the offset in view space (non projected frustrum width and height ranges [-1, 1]) 
					#if !defined(RENDERER_GL)
						float enabled = temporal_effects_enabled * taa_enabled * !debug_rendering;
						#if defined(D3D11)/* || defined(D3D12)*/ // we don't have baking support for D3D12
							enabled *= !capture_cubemap;
						#endif
						return (halton_offsets[frame_number % NUM_HALTON_OFFSETS] / back_buffer_size) / viewport.xy * 2 * enabled;
					#else
						return float2(0,0);
					#endif
				}
			#endif
		"""
	}
}
