hlsl_shaders = {
	particle_debug = {
		code="""
			#define LIT_PARTICLES_DEBUG_COLOR float4(0.01, 0.0, 0.0, 0.01)
			#define DISTORTION_PARTICLES_DEBUG_COLOR float4(0.0, 0.01, 0.0, 0.01)
			#define EMISSIVE_PARTICLES_DEBUG_COLOR float4(0.0, 0.0, 0.01, 0.01)
		"""
	}

	radiosity_normal_mapping = {
		code="""		
			#if defined(PARTICLE_LIGHTING)
				#if defined(D3D11) || defined(D3D12) || defined(GNM)
					#define BACK_LIGHTING
				#endif

				#define NEEDS_WORLD_POS

				#if defined(TESSELLATION) && defined(DISABLE_TESSELLATION)
					#undef TESSELLATION
				#endif

				#if defined(SHADOW_RECEIVING) && (defined(DISABLE_SHADOWS)|| defined(DISABLE_SUN))
					#undef SHADOW_RECEIVING
				#endif

				#if defined(LOCAL_LIGHTS) && defined(DISABLE_LOCAL_LIGHTS)
					#undef LOCAL_LIGHTS
				#endif

				#define INV_PI 0.31830988618379067153

				#if 0
					CBUFFER_START(c_particle_lighting)
						UNIFORM float  ambient_diffuse_fade_type;
						UNIFORM float3 ambient_diffuse_fade;
						UNIFORM float3 ambient_tint;
						#if !defined(DISABLE_SUN)
							UNIFORM float sun_enabled;
						#endif
						#if defined(LOCAL_LIGHTS)
							UNIFORM float n_frustum_lights;
							UNIFORM float4x4 frustum_lights[50];
						#endif
						#ifdef SHADOW_RECEIVING
							UNIFORM float4x4 sun_world_to_shadow_slice3;
						#endif
					CBUFFER_END

					DECLARE_SAMPLER_CUBE(global_diffuse_map);

					#if defined(SHADOW_RECEIVING)
						#if defined(DX10_STYLE_SAMPLERS)
							SamplerComparisonState shadow_map_sampler;
							Texture2D<float> sun_shadow_map;
						#else
							DECLARE_SAMPLER_2D(sun_shadow_map);
						#endif

						half shadow_intensity(float4 sm_pos) {
							float2 sm_resolution;
							#ifdef DX10_STYLE_SAMPLERS
								sun_shadow_map.GetDimensions(sm_resolution.x, sm_resolution.y);
							#else
								sm_resolution = float2(1024.0, 1024.0);
							#endif
							float4 tscale = float4(1.f / sm_resolution.x, 1.f / sm_resolution.y, 0.f, 0.f);

							half a = 0.0;
							for( float xx = -0.5; xx <= 0.5; xx += 1.0 ) {
								for( float yy = -0.5; yy <= 0.5; yy += 1.0 ) {
									#if defined(D3D11) || defined(D3D12)
										a += sun_shadow_map.SampleCmpLevelZero(shadow_map_sampler, sm_pos.xy + (float2( xx, yy ) * tscale.xy), sm_pos.z);
									#elif defined(GNM)
										a += sun_shadow_map.SampleCmpLOD0(shadow_map_sampler, sm_pos.xy + (float2( xx, yy ) * tscale.xy), sm_pos.z);
									#else
										a += TEX2Dproj(sun_shadow_map, sm_pos + (float4(xx, yy, 0.0, 0.0) * tscale));
									#endif
								}
							}

							return a * 0.25;
						}
					#endif

					#ifdef SCREEN_SPACE
						static const float3 hl2_basis0 = float3(0, 								   0.57735026918962576450914878050196,  0.81649658092772603273242802490196);  // float3(0,          1/sqrt(3), sqrt(2/3));
						static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485,  0.57735026918962576450914878050196, -0.40824829046386301636621401245098);  // float3(1/sqrt(2),  1/sqrt(3), -1/sqrt(6));
						static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, 0.57735026918962576450914878050196, -0.40824829046386301636621401245098);  // float3(-1/sqrt(2), 1/sqrt(3), -1/sqrt(6));
					#else
						static const float3 hl2_basis0 = float3(0, 								   -0.57735026918962576450914878050196,  0.81649658092772603273242802490196); // float3(0,           -1/sqrt(3),  sqrt(2/3));
						static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485,  -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(1/sqrt(2),   -1/sqrt(3), -1/sqrt(6));
						static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(-1/sqrt(2)), -1/sqrt(3), -1/sqrt(6));
					#endif

					void calc_basis_lighting(out float4 basis0, out float4 basis1, out float4 basis2, out float3 back_lighting, float3 wp, float3 normal, float4x4 view, float4 position) {
						// Rotate hl2 basis into world space - move to CPU
						float3 cs_hl2_basis0 = mul(hl2_basis0, to_mat3(camera_world));
						float3 cs_hl2_basis1 = mul(hl2_basis1, to_mat3(camera_world));
						float3 cs_hl2_basis2 = mul(hl2_basis2, to_mat3(camera_world));

						basis0 = float4(0.0, 0.0, 0.0, 1.0);
						basis1 = float4(0.0, 0.0, 0.0, 1.0);
						basis2 = float4(0.0, 0.0, 0.0, 1.0);
						back_lighting = float3(0.0, 0.0, 0.0);

						float3 n = normal;
						basis0.w = n.x;
						basis1.w = n.y;
						basis2.w = n.z;

						#ifdef SCREEN_SPACE
							//wp = camera_pos + view._m01_m11_m21 * camera_near_far.x + 0.3*mul(normalize(float3(wp.x, 1.f, wp.y)), to_mat3(camera_world));
							// do some form of spherical projection of the screen space particles onto the near plane
							wp = camera_pos + camera_near_far.x *mul(normalize(new_half3(wp.x, 1.f, wp.y)), to_mat3(camera_world));
						#endif

						#if defined(D3D11)
							ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint;
						#endif
						
						// TODO: make a branchless solution? Do we use this?
						float3 diffuse_tint = ambient_tint;
						float fade_type = ambient_diffuse_fade_type;
						float3 fade_params = ambient_diffuse_fade;
						[branch]
						if(fade_type != 0.0) {
							if(fade_type == 1.0) {
								diffuse_tint *= max(fade_params.x, 1.0 - exp((wp.z - fade_params.y)/fade_params.z));
							} else if(fade_type == 2.0) {
								diffuse_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - wp.z)/fade_params.z));
							} else {
								diffuse_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - wp.z)/fade_params.z));
							}
						}

						#if defined(RENDERER_GL)
							float3 V = new_half3(view[1].x, view[1].y, view[1].z);
						#else
							float3 V = view._m01_m11_m21;
						#endif

						#if !defined(DISABLE_SUN)
							if (sun_enabled == 1.0) {
								#ifdef SHADOW_RECEIVING
									float4 sm_pos = mul(float4(wp, 1), sun_world_to_shadow_slice3);
									float sun_shadow = saturate(shadow_intensity(sm_pos));
									sun_color *= sun_shadow;
								#endif

								// setup global lighting
								float3 sdir = normalize(-sun_direction);
								float3 sun_weights = saturate(new_half3(dot(sdir, cs_hl2_basis0), dot(sdir, cs_hl2_basis1), dot(sdir, cs_hl2_basis2)));
								basis0.xyz = sun_weights.x * sun_color;
								basis1.xyz = sun_weights.y * sun_color;
								basis2.xyz = sun_weights.z * sun_color;

								#if defined(BACK_LIGHTING)
									#if defined(SCREEN_SPACE)
										back_lighting = saturate(dot(sdir, -V)) * sun_color;
									#else
										back_lighting = saturate(dot(sdir, V)) * sun_color;
									#endif

									#if defined(SHADOW_RECEIVING) && defined(SUN_SCATTER_FALLOFF)
										float sf = scatter_falloff(sm_pos);
										back_lighting *= sun_shadow;
										sf = pow(sf + 0.001, 0.3);
										back_lighting *= sf; //saturate(0.05 + sf*0.95);
									#else
										back_lighting *= 0.25f;
									#endif
								#endif
							}
						#endif

						basis0.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis0, 0.0)) * diffuse_tint;
						basis1.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis1, 0.0)) * diffuse_tint;
						basis2.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis2, 0.0)) * diffuse_tint;

						// do local lights
						#if defined(LOCAL_LIGHTS)
							// const int type_omni = 0;
							const int type_spot = 1;
							int light_count = (int)n_frustum_lights;
							for (int idx = 0; idx < light_count; ++idx) {
								#if defined(RENDERER_GL)
									float3 lpos = float3(frustum_lights[idx][0].x, frustum_lights[idx][1].x, frustum_lights[idx][2].x);
									float3 ldir = float3(frustum_lights[idx][0].y, frustum_lights[idx][1].y, frustum_lights[idx][2].y);
									int ltype = (int)frustum_lights[idx][3].x;
									float3 lfalloff = float3(frustum_lights[idx][0].z, frustum_lights[idx][1].z, frustum_lights[idx][2].z);
									float2 lspotfalloff = float2(frustum_lights[idx][3].y, frustum_lights[idx][3].z);
									float3 lcol = float3(frustum_lights[idx][0].w, frustum_lights[idx][1].w, frustum_lights[idx][2].w);
								#else
									float3 lpos = frustum_lights[idx]._m00_m01_m02;
									float3 ldir = frustum_lights[idx]._m10_m11_m12;
									int ltype = (int)frustum_lights[idx]._m03;
									float3 lfalloff = frustum_lights[idx]._m20_m21_m22;
									float2 lspotfalloff = frustum_lights[idx]._m13_m23;
									float3 lcol = frustum_lights[idx]._m30_m31_m32;
								#endif

								float3 light_vector = lpos - wp; // This used to be center.. validate if not wp is better
								float l = length(light_vector) + 0.00001;
								float attn = light_attenuation(l, lfalloff.x, lfalloff.y);
								light_vector /= l;
								float spot_angle = 1.0 - dot(light_vector, -ldir);
								attn *= ltype == type_spot ? (spot_angle > lspotfalloff.x ? 1.0 - saturate((spot_angle-lspotfalloff.x)*lspotfalloff.y) : 1.0) : 1.0;

								float3 lambert_atten = saturate(float3(dot(light_vector, cs_hl2_basis0), dot(light_vector, cs_hl2_basis1), dot(light_vector, cs_hl2_basis2)));
								float3 col = attn * lcol;
								basis0.xyz += lambert_atten.x * col;
								basis1.xyz += lambert_atten.y * col;
								basis2.xyz += lambert_atten.z * col;
								#if defined(BACK_LIGHTING)
									back_lighting += saturate(dot(light_vector, V)) * col;// * 0.25;
								#endif
							}
						#endif
					}
				#else
					#if defined(LOCAL_LIGHTS)
						DECLARE_CLUSTER_DATA(cs_cluster_buffer);
						DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer);
						DECLARE_LIGHT_DATA(cs_light_data_buffer);
						#if defined(SHADOW_RECEIVING)
							DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer);
							
							DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas);
							#if !defined(DISABLE_SUN)
								DECLARE_COMPARISON_SAMPLER_2D(sun_shadow_map);
								DECLARE_COMPARISON_SAMPLER_2D(static_sun_shadow_map);
							#endif
						#endif
					#endif
					#if !defined(CALCULATE_LIGHTING)
						CBUFFER_START(lighting_data)
							UNIFORM float3 ambient_tint;

							#if !defined(DISABLE_SUN)
								UNIFORM float sun_shadows_enabled;
								UNIFORM float sun_enabled;
								
								#if defined(SHADOW_RECEIVING)
									UNIFORM float ssm_enabled;
									UNIFORM float4x4 ssm_shadow_rotation;
								#endif
							#endif

							UNIFORM float  ambient_diffuse_fade_type;
							UNIFORM float3 ambient_diffuse_fade; // min multiplier, height offset, falloff
						CBUFFER_END
					#endif

					DECLARE_SAMPLER_CUBE(global_diffuse_map);

					#ifdef SCREEN_SPACE
						static const float3 hl2_basis0 = float3(0, 								   0.57735026918962576450914878050196,  0.81649658092772603273242802490196);  // float3(0,          1/sqrt(3), sqrt(2/3));
						static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485,  0.57735026918962576450914878050196, -0.40824829046386301636621401245098);  // float3(1/sqrt(2),  1/sqrt(3), -1/sqrt(6));
						static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, 0.57735026918962576450914878050196, -0.40824829046386301636621401245098);  // float3(-1/sqrt(2), 1/sqrt(3), -1/sqrt(6));
					#else
						static const float3 hl2_basis0 = float3(0, 								   -0.57735026918962576450914878050196,  0.81649658092772603273242802490196); // float3(0,           -1/sqrt(3),  sqrt(2/3));
						static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485,  -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(1/sqrt(2),   -1/sqrt(3), -1/sqrt(6));
						static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(-1/sqrt(2)), -1/sqrt(3), -1/sqrt(6));
					#endif

					// Rotate hl2 basis into world space - move to CPU
					static const float3 cs_hl2_basis0 = mul(hl2_basis0, to_mat3(camera_world));
					static const float3 cs_hl2_basis1 = mul(hl2_basis1, to_mat3(camera_world));
					static const float3 cs_hl2_basis2 = mul(hl2_basis2, to_mat3(camera_world));

					void accumulate_basis_lighting(float3 L, float3 V, float3 light_color, float attn, inout float4 basis0, inout float4 basis1, inout float4 basis2, inout float3 back_lighting) {
						float3 lambert_atten = saturate(float3(dot(L, cs_hl2_basis0), dot(L, cs_hl2_basis1), dot(L, cs_hl2_basis2)));
						float3 color = light_color * attn;
						basis0.xyz += lambert_atten.x * color;
						basis1.xyz += lambert_atten.y * color;
						basis2.xyz += lambert_atten.z * color;
						#if defined(BACK_LIGHTING)
							back_lighting += saturate(dot(L, V)) * color;// * 0.25;
						#endif
					}

					void clustered_shading(CLUSTER_DATA_TYPE cluster_data,
						LIGHT_INDEX_DATA_TYPE light_index_data,
						LIGHT_DATA_TYPE light_data,
						#if defined(SHADOW_RECEIVING)
							LIGHT_SHADOW_MATRICES_TYPE light_shadow_matrices,
							ComparisonSampler2D local_lights_shadow_atlas,
						#endif
						const float3 world_pos,
						const float3 V,
						const float3 N,
						const float2 screen_pos,
						const float depth,
						inout float4 basis0,
						inout float4 basis1,
						inout float4 basis2,
						inout float3 back_lighting)
					{
						if (depth > cs_cluster_max_depth_inv_max_depth.x)
							return;

						uint2 cluster_info;
						sample_cluster_data(cluster_data, screen_pos, depth, cluster_info);

						uint light_index = cluster_info.x;
						int point_light_count = int(cluster_info.y & 0x00FFU);
						for (int pl = 0; pl < point_light_count; ++pl) {
							uint index;
							sample_light_index_data(light_index_data, light_index, index);
							float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir;
							float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5;
							float shadow_fade;
							sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade);

							float3 L = light_position - world_pos;
							float len_L = length(L) + 0.00001f;
							L /= len_L;

							float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y);
							accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting);
							//translucency += calculate_translucency(L, V, N, attn * density, base_color, light_color, density);
							++light_index;
						}

						int spot_light_count = int((cluster_info.y >> 16U) & 0x00FFU);
						for (int sl = 0; sl < spot_light_count; ++sl) {
							uint index;
							sample_light_index_data(light_index_data, light_index, index);
							float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir;
							float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5;
							float shadow_fade;
							sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade);

							float3 L = light_position - world_pos;
							float len_L = length(L) + 0.00001f;
							L /= len_L;

							float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y);
							attn *= spot_angle_attenuation(dot(-L, spot_dir), spot_light_falloff.x, spot_light_falloff.y);
							accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting);
							//translucency += calculate_translucency(L, V, N, attn * density, base_color, light_color, density);

							++light_index;
						}

						int shadow_casting_point_light_count = int((cluster_info.y >> 8U) & 0x00FFU);
						for (int scpl = 0; scpl < shadow_casting_point_light_count; ++scpl) {
							uint index;
							sample_light_index_data(light_index_data, light_index, index);
							float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir;
							float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5;
							float shadow_fade;
							sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade);

							float3 L = light_position - world_pos;
							float len_L = length(L) + 0.00001f;
							L /= len_L;

							float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y);

							#if defined(SHADOW_RECEIVING)
								// Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct
								// shadow map index to get the correct world position to shadow map matrix.
								float3 shadow_L = -L;
								int3 is_positive = int3(greaterThan(shadow_L, float3(0.0, 0.0, 0.0)));
								float3 abs_shadow_L = abs(shadow_L);
								int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2];

								// The shadows from the faces of an onmni light are populated in the following order and directions.
								//	float3(-1.0f,  0.0f,  0.0f),
								//	float3( 1.0f,  0.0f,  0.0f),
								//	float3( 0.0f, -1.0f,  0.0f),
								//	float3( 0.0f,  1.0f,  0.0f),
								//	float3( 0.0f,  0.0f, -1.0f)
								//	float3( 0.0f,  0.0f,  1.0f),
								//fix this crap about the sm_index potentially being -1.0 which indicates that the light isn't casting any shadows....
								float sm_index = test_index == 0? sm_index0 :
												test_index == 1? sm_index1 :
												test_index == 2? sm_index2 :
												test_index == 3? sm_index3 :
												test_index == 4? sm_index4 :
												sm_index5;

								float shadow_intensity = 1.0f;
								if (sm_index != -1.0f) {
									float4x4 world_to_sm;
									sample_light_shadow_matrix(light_shadow_matrices, uint(sm_index), world_to_sm);
									float4 sm_pos = mul(float4(world_pos, 1.0), world_to_sm);
									sm_pos.xyz /= sm_pos.w;
									sm_pos.z = apply_local_shadow_depth_comparison_bias(depth, sm_pos.z);
									shadow_intensity = shadow_intensity_2d(local_lights_shadow_atlas, cs_shadow_atlas_size.xy, sm_pos.xy, sm_pos.z);
									shadow_intensity = (shadow_intensity - 1.0) * shadow_fade + 1.0;
								}
								attn *= shadow_intensity;
							#endif
							accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting);
							//translucency += calculate_translucency(L, V, N, attn * lerp(lerp(0.5, 1.0, shadow_intensity), shadow_intensity, density), base_color, light_color, density);

							++light_index;
						}

						int shadow_casting_spot_light_count = int((cluster_info.y >> 24U) & 0x00FFU);
						for (int scsl = 0; scsl < shadow_casting_spot_light_count; ++scsl) {
							uint index;
							sample_light_index_data(light_index_data, light_index, index);
							float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir;
							float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5;
							float shadow_fade;
							sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade);

							float3 L = light_position - world_pos;
							float len_L = length(L) + 0.00001f;
							L /= len_L;

							float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y);
							attn *= spot_angle_attenuation(dot(-L, spot_dir), spot_light_falloff.x, spot_light_falloff.y);

							#if defined(SHADOW_RECEIVING)
								float4x4 world_to_sm;
								sample_light_shadow_matrix(light_shadow_matrices, uint(sm_index5), world_to_sm);
								float4 sm_pos = mul(float4(world_pos, 1.0), world_to_sm);
								sm_pos.xyz /= sm_pos.w;
								sm_pos.z = apply_local_shadow_depth_comparison_bias(depth, sm_pos.z);
								float shadow_intensity = shadow_intensity_2d(local_lights_shadow_atlas, cs_shadow_atlas_size.xy, sm_pos.xy, sm_pos.z);
								shadow_intensity = (shadow_intensity - 1.0) * shadow_fade + 1.0;
								attn *= shadow_intensity;
							#endif

							accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting);
							//translucency += calculate_translucency(L, V, N, attn * lerp(lerp(0.5, 1.0, shadow_intensity), shadow_intensity, density), base_color, light_color, density);

							++light_index;
						}
					}

					void calc_basis_lighting(out float4 basis0, out float4 basis1, out float4 basis2, out float3 back_lighting, float3 wp, float3 N, float4x4 view, float4 position) {
						basis0 = float4(0.0, 0.0, 0.0, 1.0);
						basis1 = float4(0.0, 0.0, 0.0, 1.0);
						basis2 = float4(0.0, 0.0, 0.0, 1.0);
						back_lighting = float3(0.0, 0.0, 0.0);

						basis0.w = N.x;
						basis1.w = N.y;
						basis2.w = N.z;

						#ifdef SCREEN_SPACE
							//wp = camera_pos + view._m01_m11_m21 * camera_near_far.x + 0.3*mul(normalize(float3(wp.x, 1.f, wp.y)), to_mat3(camera_world));
							// do some form of spherical projection of the screen space particles onto the near plane
							wp = camera_pos + camera_near_far.x * mul(normalize(new_half3(wp.x, 1.f, wp.y)), to_mat3(camera_world));
						#endif

						#if defined(D3D11)
							ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint;
						#endif

						// TODO: make a branchless solution? Do we use this?
						float3 diffuse_tint = ambient_tint;
						float fade_type = ambient_diffuse_fade_type;
						float3 fade_params = ambient_diffuse_fade;
						[branch]
						if(fade_type != 0.0) {
							if(fade_type == 1.0) {
								diffuse_tint *= max(fade_params.x, 1.0 - exp((wp.z - fade_params.y)/fade_params.z));
							} else if(fade_type == 2.0) {
								diffuse_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - wp.z)/fade_params.z));
							} else {
								diffuse_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - wp.z)/fade_params.z));
							}
						}

						const float3 view_dir = camera_world._m30_m31_m32 - wp;
						const float3 V = normalize(view_dir);
						#if defined(SHADOW_RECEIVING) || defined(LOCAL_LIGHTS)
							const float3 camera_dir = camera_world._m10_m11_m12;
							const float depth = dot(-view_dir, camera_dir);
							// const float3 shadow_biased_pos = wp - (depth - apply_shadow_bias(depth)) * (-V);
						#endif

						#if !defined(DISABLE_SUN)
							if (sun_enabled) {
								#if defined(SHADOW_RECEIVING)
									float shadow = 1.0;
									if(sun_shadows_enabled && sun_shadows) {
										shadow = saturate(calculate_shadow_intensity(sun_shadow_map, static_sun_shadow_map, wp, depth));
									} else if(ssm_enabled) {
										shadow = saturate(calculate_static_shadow_intensity(static_sun_shadow_map, wp, depth));
									}
									sun_color *= shadow;
								#endif

								// setup global lighting
								float3 L = normalize(-sun_direction);
								float3 sun_weights = saturate(new_half3(dot(L, cs_hl2_basis0), dot(L, cs_hl2_basis1), dot(L, cs_hl2_basis2)));
								basis0.xyz = sun_weights.x * sun_color;
								basis1.xyz = sun_weights.y * sun_color;
								basis2.xyz = sun_weights.z * sun_color;

								#if defined(BACK_LIGHTING)
									#if defined(SCREEN_SPACE)
										back_lighting = saturate(dot(L, -V)) * sun_color;
									#else
										back_lighting = saturate(dot(L, V)) * sun_color;
									#endif

									#if defined(SHADOW_RECEIVING) && defined(SUN_SCATTER_FALLOFF)
										float sf = scatter_falloff(sm_pos);
										back_lighting *= sun_shadow;
										sf = pow(sf + 0.001, 0.3);
										back_lighting *= sf; //saturate(0.05 + sf*0.95);
									#else
										back_lighting *= 0.25f;
									#endif
								#endif
							}
						#endif

						basis0.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis0, 0.0)) * diffuse_tint;
						basis1.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis1, 0.0)) * diffuse_tint;
						basis2.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis2, 0.0)) * diffuse_tint;

						#if defined(LOCAL_LIGHTS)
							// Transform Normalized Display Coordinates to view port coordinates 
							const float2 clip_space = (float2(position.x, -position.y)/position.w + 1.0)*0.5;
							const float2 view_space = clip_space * (back_buffer_size * viewport.xy - 1.0) + 0.5; // I assume that SV_POSITION is in range of 0.5 to viewport.width - 0.5
							
							clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, 
								#if defined(SHADOW_RECEIVING)
									cs_light_shadow_matrices_buffer, local_lights_shadow_atlas,
								#endif
								wp, V, N, view_space, depth, basis0, basis1, basis2, back_lighting);
						#endif
					}
				#endif

				float3 calc_lighting(const float4 base_color_alpha, const float3 N, const float3 back_lighting, const float3 basis0, const float3 basis1, const float3 basis2) {
					float3 weights = saturate(float3(dot(N, hl2_basis0), dot(N, hl2_basis1), dot(N, hl2_basis2)));						
					float3 light = weights.x * basis0 + weights.y * basis1 + weights.z * basis2;						

					#if defined(BACK_LIGHTING)
						float back_light_scale = saturate(1.0-(base_color_alpha.a*0.5+0.25));
						back_light_scale *= back_light_scale;
						light += back_lighting * back_light_scale;
					#endif

					return base_color_alpha.rgb * light * INV_PI;
				}
			#endif
		"""	
	}
}