group = "Output"
display_name = "Standard Base"
output_node = true
render_configs = ["core/stingray_renderer/renderer"]

inputs = {
	"aee6e47b-be7b-4d67-a123-2ab5d660b94e" = {
		name = "vertex_offset"
		display_name = "Position offset"
		is_required = false
		type = { vector3: ["HAS_VERTEX_OFFSET"] }
		domain = "vertex"
	}

	"aca690cb-6305-4a2f-bf3d-69183a493db3" = {
		name = "base_color"
		is_required = false
		display_name = "Base Color"
		type = { vector3: ["HAS_BASE_COLOR"] }
		domain = "pixel"
	}

	"34259752-b962-4b65-92c3-903a57338519" = {
		name = "opacity"
		is_required = false
		display_name = "Opacity"
		type = { scalar: ["HAS_OPACITY"] }
		domain = "pixel"
	}

	"7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = {
		name = "opacity_threshold"
		is_required = false
		display_name = "Opacity Threshold"
		type = { scalar: ["HAS_OPACITY_THRESHOLD"] }
		domain = "pixel"
	}

	"b1c86408-aacb-4466-b754-ddcf37a3a2c8" = {
		is_required = false
		name = "normal"
		display_name = "Normal"
		type = { vector3: ["HAS_NORMAL"] }
		domain = "pixel"
	}

	"ad5e052f-d316-4a0f-8b79-53c38204d61b" = {
		is_required = false
		name = "metallic"
		display_name = "Metallic"
		type = { scalar: ["HAS_METALLIC"] }
		domain = "pixel"
	}

	"36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = {
		is_required = false
		name = "roughness"
		display_name = "Roughness"
		type = { scalar: ["HAS_ROUGHNESS"] }
		domain = "pixel"
	}

	"1164a5ef-4563-4795-b3b5-42825d6df037" = {
		is_required = false
		name = "emissive"
		display_name = "Emissive"
		type = { vector3: ["HAS_EMISSIVE"] }
		domain = "pixel"
	}

	"59fd1cf4-f736-470d-8510-1dd7c016639e" = {
		is_required = false
		name = "ambient_occlusion"
		display_name = "Ambient Occlusion"
		type = { scalar: ["HAS_AMBIENT_OCCLUSION"] }
		domain = "pixel"
	}

	"0544ddb6-e168-452d-86f2-42a79e8c98e3" = {
		is_required = false
		name = "sss_strength"
		display_name = "Skin SSS"
		type = { scalar: ["SKIN", "HAS_SKIN_SSS_STRENGTH"] }
		domain = "pixel"
	}

	"cc5a1773-27a2-430b-96d2-d81815d97feb" = {
		is_required = false
		name = "refraction"
		display_name = "Refraction"
		type = { scalar: ["HAS_REFRACTION"] }
		domain = "pixel"
	}

	"20346ea6-ecb0-41c1-bdcc-35bbe3ce1a5c" = {
		is_required = false
		name = "density"
		display_name = "Density"
		type = { scalar: ["HAS_DENSITY"] }
		domain = "pixel"
	}
}

options = {
	"b2c7c0d2-beff-4b1a-a9d4-068a507625a2" = "USE_FBX_PERMUTATIONS"
	"d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED"
	"8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "CULL_NONE"
	"c198c109-2cdf-49ee-af18-a982c23e2729" = "CULL_FRONT"
	"2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL"
	"dd7fcf97-0627-48ab-b29a-95b5685bb123" = "TRANSPARENT"
	"3b55d6c6-4398-4dbc-b9ef-570aff8696ae" = "TRANSPARENT_FADE"
	"b5bb2062-c8fa-43c5-8657-493a0be6860c" = "SKINNED_DISABLED"
	"901b44ce-d128-498a-9912-32cd9635c556" = "HAS_FOV"
	"da4e717b-5d0d-47fa-93b0-01c988f8f3ff" = "LOCK_NORMAL_ROTATION"
	"979cb7cf-c255-42d0-a389-78fc0fb0539f" = "DEFERRED_DECALS_GROUP_1"
	"d557b597-fa90-46b4-a751-eb51ae61ba5b" = "DEFERRED_DECALS_GROUP_2"
	"fe20cc01-4cec-4217-98a0-07220ad3add9" = "DEFERRED_DECALS_GROUP_3"
	"524a5842-23b7-46d1-ab22-cb3a14746ce0" = "USE_GLOBAL_ROUGHNESS_MULTIPLIER"
	"11256995-4805-4018-bd6b-9214414fd363" = "NORMAL_FLIP_DISABLED"
	"580cc6eb-3591-4c71-aa50-528af18ba160" = "JITTER_TRANSPARENCY"
	"b649332d-7a8a-42b3-a809-6d0eb9c7c070" = "GBUFFER_POST_OUTLINE"
	"08e965b9-90ed-4046-b314-c97ea7e7f30e" = "OPAQUE_FORWARD_DISABLED"
}

ui = [
	{
		type = "drop_down"
		display_name = "Normals In"
		options = {
			"Tangent Space"	= "00000000-0000-0000-0000-000000000000"
			"World Space"	= "2b136447-676e-4943-997b-04a28ae68497"
		}
		default = "00000000-0000-0000-0000-000000000000"
	}
	{
		type = "drop_down"
		display_name = "Blend Mode"
		options = {
			"Opaque"			= "00000000-0000-0000-0000-000000000000"
			"Transparent"		= "dd7fcf97-0627-48ab-b29a-95b5685bb123"
			"Transparent Fade"	= "3b55d6c6-4398-4dbc-b9ef-570aff8696ae"
		}
		default = "00000000-0000-0000-0000-000000000000"
	}
	{
		type = "drop_down"
		display_name = "Face Culling"
		options = {
			"Back"					= "00000000-0000-0000-0000-000000000000"
			"Front"					= "c198c109-2cdf-49ee-af18-a982c23e2729"
			"None (double sided)"	= "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46"
		}
		default = "00000000-0000-0000-0000-000000000000"
	}	

	{
		type = "drop_down"
		display_name = "Decal Group"
		options = {
			"Group 0" = "00000000-0000-0000-0000-000000000000"
			"Group 1" = "979cb7cf-c255-42d0-a389-78fc0fb0539f"
			"Group 2" = "d557b597-fa90-46b4-a751-eb51ae61ba5b"
			"Group 3" = "fe20cc01-4cec-4217-98a0-07220ad3add9"
		}
		default = "00000000-0000-0000-0000-000000000000"
	}
	
	{ type = "checkbox" display_name = "Disable Skinning"						option = "b5bb2062-c8fa-43c5-8657-493a0be6860c" }
	{ type = "checkbox" display_name = "Instancing" 							option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" }
	{ type = "checkbox" display_name = "Custom FOV" 							option = "901b44ce-d128-498a-9912-32cd9635c556" }
	{ type = "checkbox" display_name = "Avoid Tangent Space Transform" 			option = "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" }
	{ type = "checkbox" display_name = "Use Global Roughness Multiplier" 		option = "524a5842-23b7-46d1-ab22-cb3a14746ce0" }
	{ type = "checkbox" display_name = "Normal Flip Disabled (double sided)" 	option = "11256995-4805-4018-bd6b-9214414fd363" }
	{ type = "checkbox" display_name = "Jitter Transparency" 					option = "580cc6eb-3591-4c71-aa50-528af18ba160" }
	{ type = "checkbox" display_name = "Gbuffer Post-Outline" 					option = "b649332d-7a8a-42b3-a809-6d0eb9c7c070" }
	{ type = "checkbox" display_name = "Disable Opaque Forward"				 	option = "08e965b9-90ed-4046-b314-c97ea7e7f30e" }
]


render_state = {
	culling_base_default = {
		inherit: ["core/stingray_renderer/shader_libraries/common#default"]
		state: {
			"defined(CULL_NONE)" = {
				cull_mode = "cull_none"
			}
			"defined(CULL_FRONT)" = {
				cull_mode = "cull_ccw"
			}
			"!defined(CULL_NONE) && !defined(CULL_FRONT)" = {
				cull_mode = "cull_cw"
			}
		}
	}

	culling_base_gbuffer = {
		inherit: ["core/stingray_renderer/shader_libraries/common#gbuffer_material"]
		state: {			
			"defined(CULL_NONE)" = {
				cull_mode = "cull_none"
			}
			"defined(CULL_FRONT)" = {
				cull_mode = "cull_ccw"
			}
			"!defined(CULL_NONE) && !defined(CULL_FRONT)" = {
				cull_mode = "cull_cw"	
			}
		}
	}

	gbuffer_material = {
		inherit: ["culling_base_gbuffer"]
		state: {
			"defined(HAS_FOV)" = {
				stencil_enable = "true"
				stencil_func = "always"
				stencil_fail = "stencil_op_keep"
				stencil_mask = "0xf8"
				stencil_pass = "stencil_op_replace"
				stencil_write_mask = "0xf8"
				stencil_z_fail = "stencil_op_keep"

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

				defined_DEFERRED_DECALS_GROUP_1 = {
					defined_SKIN = {
						stencil_ref = "0xa8"
					}
					ndefined_SKIN = {
						stencil_ref = "0xa0"
					}
				}
				ndefined_DEFERRED_DECALS_GROUP_1 = {
					defined_DEFERRED_DECALS_GROUP_2 = {
						defined_SKIN = {
							stencil_ref = "0xc8"
						}
						ndefined_SKIN = {
							stencil_ref = "0xc0"
						}
					}
					ndefined_DEFERRED_DECALS_GROUP_2 = {
						defined_DEFERRED_DECALS_GROUP_3 = {
							defined_SKIN = {
								stencil_ref = "0xe8"
							}
							ndefined_SKIN = {
								stencil_ref = "0xe0"
							}
						}
						ndefined_DEFERRED_DECALS_GROUP_3 = {
							defined_SKIN = {
								stencil_ref = "0x88"
							}
							ndefined_SKIN = {
								stencil_ref = "0x80"
							}
						}
					}
				}
			}
		}
	}

	emissive = {
		inherit: ["culling_base_default"]
		state: {
			z_write_enable = "false"
			z_func = "less_equal"
			blend_enable = "true"
			blend_op = "blend_op_add"
			dest_blend = "blend_one"
			src_blend = "blend_one"
		}
	}

	transparent_base = {
		state : {
			z_write_enable = "false"
			// with refraction we are essentially taking over the blending in our shader, so we disable alpha blending
			"!defined(HAS_REFRACTION)" = {
				blend_enable = "true"
				blend_op = "blend_op_add"
				dest_blend = "blend_inv_src_alpha"
				src_blend = "blend_one"
			}
		
			"defined(HAS_FOV)" = {
				stencil_enable = "true"
				stencil_func = "always"
				stencil_fail = "stencil_op_keep"
				stencil_mask = "0x80"
				stencil_pass = "stencil_op_replace"
				stencil_ref = "0x80"
				stencil_write_mask = "0x80"
				stencil_z_fail = "stencil_op_keep"

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

	transparent = {
		inherit: ["culling_base_default", "transparent_base"]
	}

	transparent_double_sided_cw = {
		inherit: ["core/stingray_renderer/shader_libraries/common#default", "transparent_base"]
		state : {
			cull_mode = "cull_cw"
		}
	}

	transparent_double_sided_ccw = {
		inherit: ["core/stingray_renderer/shader_libraries/common#default", "transparent_base"]
		state : {
			cull_mode = "cull_ccw"
		}
	}

	wireframe = {
		inherit: ["transparent"]
		state: {
			fill_mode = "fill_wireframe"
			src_blend = "blend_src_alpha"
			cull_mode = "cull_none"
			"on_renderer(D3D11, D3D12)" = {
				depth_bias = "-1"
				depth_bias_clamp = "-0.00015"
				slope_scale_depth_bias = "-2.0"
			}
		}
	}

	depth_only = {
		inherit: ["culling_base_default"]
		state: {
			write_mask0 = "0x0"
			write_mask1 = "0x0"
			write_mask2 = "0x0"
			write_mask3 = "0x0"
		}
	}	

	outline_stencil_write = {
		inherit: ["culling_base_default"]
		state: {
			z_enable = "false"
			z_write_enable = "false"
			
			stencil_enable = "true"
			stencil_func = "always"
			stencil_fail = "stencil_op_keep"
			stencil_mask = "0x80"
			stencil_pass = "stencil_op_replace"
			stencil_ref = "0x80"
			stencil_write_mask = "0x80"
			stencil_z_fail = "stencil_op_replace"

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

	outline_stencil_write_z = {
		inherit: ["culling_base_default"]
		state: {
			z_enable = "true"
			z_write_enable = "false"

			stencil_enable = "true"
			stencil_func = "always"
			stencil_fail = "stencil_op_keep"
			stencil_mask = "0x80"
			stencil_pass = "stencil_op_replace"
			stencil_ref = "0x80"
			stencil_write_mask = "0x80"
			stencil_z_fail = "stencil_op_replace"

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

	outline_mask_write = {
		inherit: ["culling_base_default"]
		state: {
			z_enable = "true"
			z_write_enable = "false"
			stencil_enable = "false"
			write_mask0 = "red"
		}
	}


	shadow_caster = {
		inherit: ["depth_only"]
		state: {
			"on_renderer(D3D11, D3D12)" = {
				depth_bias = "0xff"
				slope_scale_depth_bias = "2.5"
			}
			"on_renderer(GNM)" = {
				offset_front = "0.0001"
				offset_scale_front = "40.0"
				offset_back = "0.0001"
				offset_scale_back = "40.0"
			}
			"on_renderer(GL)" = {
				offset_factor = "1.0"
				offset_units = "1024.0"
				depth_bias_enable = "true"
			}
		}
	}

	material_transfer = {
		inherit: ["core/stingray_renderer/shader_libraries/common#default"]
		state: {
			cull_mode = "cull_none"
			z_write_enable = "false"
			z_enable = "false"
		}
	}
}

sampler_state = {
	shadow_map = {
		inherit: ["core/stingray_renderer/shader_libraries/common#clamp_point"]
		states = {
			"on_renderer(D3D11, D3D12)" = {
				comparison_func = "less"
				filter = "comparison_min_mag_linear_mip_point"
			}
			"on_renderer(GNM)" = {
				comparison_func = "less"
				filter = "min_mag_mip_linear"

			}
			"on_renderer(GL)" = {
				comparison_func = "less"
				filter = "min_mag_linear"
			}
		}
	}

	clamp_point_no_mip = {
		inherits: ["core/stingray_renderer/shader_libraries/common#clamp"]
		states = {
			"on_renderer(GL)" = {
				filter = "min_mag_point"
			}
			"on_renderer(D3D11, D3D12)" = {
				filter = "min_mag_mip_point"
			}
			"on_renderer(GNM)" = {
				filter = "min_mag_mip_point"
			}
		}
	}
}

channels = 	{
	"(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)": {
		vertex_tangent = { type = "float3" semantic = "TANGENT" domain = "vertex" }
		vertex_binormal = { type = "float3" semantic = "BINORMAL" domain = "vertex" }

		tsm0 = { type = "float3" domains = ["vertex", "pixel"] }
		tsm1 = { type = "float3" domains = ["vertex", "pixel"] }
		tsm2 = { type = "float3" domains = ["vertex", "pixel"] }
	}

	"defined(NEEDS_EYE_VECTOR)": {
		eye_vector = { type = "float3" domain = "vertex" }
	}

	"!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)": {
		world_space_normal = { type = "float3" domains = ["vertex", "pixel"] }
	}

	"defined(MOTION_BLUR)": {
		last_clip_position = { type = "float3" domains = ["vertex", "pixel"] }
	}

	vertex_position = { type = "float4" domain = "vertex" }
	vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" }

	"defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING)": {
		vertex_color1 = { type = "float4" semantic = "COLOR1" domains = ["vertex"] }
		baked_light = { type = "float3" domains = ["vertex", "pixel"] }
	}

	"defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": {
		lightmap_uv_input = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] }
		lightmap_uv = { type = "float2" domains = ["vertex", "pixel"] }
	}
	
	"defined(MATERIAL_TRANSFER)": {
		lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] }
	}

	"defined(NEEDS_UNSKINNED_WORLD_POS)": {
		unskinned_world_pos = { type = "float3" domains = ["vertex", "pixel"] }
		unskinned_normal = { type = "float3" domains = ["vertex", "pixel"] }
	}

	"defined(NEEDS_PIXEL_DEPTH)": {
		pixel_depth = { type = "float" domain = "pixel" }
	}

	"defined(NEEDS_SCREEN_POS)": {
		screen_pos = { type = "float2" domain = "pixel" }
	}

	"defined(NEEDS_SUN_SHADOW_MASK)": {
	//"defined(NEEDS_SUN_SHADOW_MASK) && (defined(EMISSIVE_PASS) || defined(TRANSPARENT) || defined(TRANSPARENT_FADE))": {	
		sun_shadow_mask = { type = "float" domain = "pixel" }
	}
}

//log_permutations = true
permutation_sets = {
	vertex_modifiers = [
		{ if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } }
		{ if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } }
		{ if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } }
		{ if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"]  stages: ["vertex"] } }
		{ default = true }
	]

	instanced_modifiers = [
		// { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] }
		{ default = true }
	]

	lightmap_modifiers = [
		// { if: "lightmap_format() == directional_irradiance" define: ["HAS_DIRECTIONAL_LIGHTMAPS"] }
		{ default = true }
	]

	non_instanced_modifiers = [
		// { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] permute_with: "lightmap_modifiers" }
		// { if: "mesh_baked_lighting_type() == vertex" define: ["HAS_VERTEX_BAKED_DIFFUSE_LIGHTING"] }
		{ permute_with: "vertex_modifiers" }  
	]

	instanced_and_non_instanced = [
		{ if: "defined(INSTANCED)" permute_with: "instanced_modifiers" }
		{ if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" }
	]
	
	default = [
		// FBX Standard material exclusive permutations, these will only compile of the
		// 'USE_FBX_PERMUTATIONS' option is set, which it is in the shader used for imported fbx files.
		//{ if: "defined(USE_FBX_PERMUTATIONS) && is_any_material_variable_set(use_emissive_map, emissive)" define: ["FBX_EMISSIVE"] permute_with: "instanced_and_non_instanced" }

		// Normal default permutation set
		{ if: "on_renderer(D3D11, D3D12, GNM, GL) && render_cap(development) && render_setting(mipmap_visualization)" defines=["MIPMAP_LEVEL_VISUALIZATION"] permute_with: "instanced_and_non_instanced" }
		{ if: "on_renderer(D3D11, D3D12, GNM, GL) && (defined(TRANSPARENT) || defined(TRANSPARENT_FADE)) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] permute_with: "instanced_and_non_instanced" }
		{ permute_with: "instanced_and_non_instanced" }
	]

	shadow_caster = [
		{ if: "defined(INSTANCED)" }
		{ if: "!defined(INSTANCED)" permute_with: "vertex_modifiers" }
	]
}

//log_permutations = true
//log_constant_buffers = true
shader_contexts = {
	shadow_caster = {
		passes_sort_mode = "immediate"
		compile_with = [
			{ if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "shadow_caster" }
		]

		passes = [
			{ code_block="depth_only" render_state="shadow_caster" }
		]
	}

	//material_transfer = {
	//	passes_sort_mode = "immediate"
	//	compile_with = [
	//		{ if: "on_renderer(D3D11, D3D12)" }
	//	]
	//
	//	passes = [
	//		{ code_block="gbuffer_base" defines=["MATERIAL_TRANSFER"] render_state="material_transfer" }
	//	]
	//}

	default = {
		passes_sort_mode = "deferred"
		compile_with = [
			{ if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: [
				// TODO: create permutation for when local lights is off.
				{ permute_with: "default" }
			] }
		]

		passes = [
			{ if: "(defined(TRANSPARENT) || defined(TRANSPARENT_FADE))" then: [
				{ if: "defined(CULL_NONE)" then: [
					// TODO: render transparency in two passes makes the last pass not being sorted correctly with particles
					//{ layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent_double_sided_ccw" }
					//{ layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent_double_sided_cw" }
					{ layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent" }
				] else: [
					{ layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent" }
				]}
			] else: [
				{ if: "defined(GBUFFER_POST_OUTLINE)"  then: [
					{ layer="gbuffer_post_outline" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" }
				] else: [
					{ if: "defined(HAS_FOV)" then: [
						{ layer="gbuffer_fpr" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" }
					] else: [
						{ if: "defined(HAS_OPACITY) || defined(JITTER_TRANSPARENCY)" then: [
							{ layer="gbuffer_alpha_masked" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" }
						] else: [
							{ layer="gbuffer" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" }
						]}
					]}
				]}
				
				// This bit of logic is a bit complicated. The gist of it is that we want to disable this pass
				// for materials that has a value connected on emissive for all permutations, but should have it
				// discarded for all but the special permutations with the define FBX_EMISSIVE
				{ if: "defined(HAS_EMISSIVE) && (!defined(USE_FBX_PERMUTATIONS) || defined(FBX_EMISSIVE))" then: [
					{ layer="emissive" code_block="gbuffer_base" defines={ macros: ["EMISSIVE_PASS"] stages: ["pixel"] } render_state="emissive" }
				]}

				{ if: "!defined(OPAQUE_FORWARD_DISABLED)" then: [
					{ layer="opaque_forward" code_block="gbuffer_base" defines=["OPAQUE_FORWARD" "CALCULATE_LIGHTING" "MOTION_BLUR"] render_state="culling_base_default" }
				]}
			]}

			// Note we define MOTION_VECTOR for these additional passes to ensure we share constant buffer for bone stransforms with the opaque gbuffer pass
			// For transparent surfaces the MOTION_VECTOR define gets undefined in the SKINNED code block
			{ if: "!on_renderer(GL)" then: [
				{ layer="wireframe" code_block="depth_only" defines=["DRAW_WIREFRAME" "MOTION_BLUR"] render_state="wireframe" branch_key="dev_wireframe" }
			]}

			{ layer="outline" code_block="depth_only" define="DRAW_OUTLINE" render_state="outline_stencil_write" branch_key="outline_unit" }
			{ layer="outline" code_block="depth_only" define="DRAW_OUTLINE" render_state="outline_stencil_write_z" branch_key="outline_unit_z" }

			// not used
			// { layer="outline_mask" code_block="depth_only" defines=["DRAW_OUTLINE" "OUTLINE_MASK_WRITE"] render_state="outline_mask_write" branch_key="outline_unit_z_fail" }
			// { layer="outline" code_block="depth_only" defines=["DRAW_OUTLINE" "OUTLINE_MASK_READ"] render_state="outline_stencil_write" branch_key="outline_unit_z_fail" }
		]
	}
}

code_blocks = {
	gbuffer_base = {
		include:[
			"core/stingray_renderer/shader_libraries/common#common",
			"core/stingray_renderer/shader_libraries/common#gbuffer_access",
			"core/stingray_renderer/shader_libraries/common#skinning",
			"core/stingray_renderer/shader_libraries/common#taa_offsets",
			"core/stingray_renderer/shader_libraries/lighting_common#brdf",
			"core/stingray_renderer/shader_libraries/common#fog",
			"core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias",
			"core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map_filtering",
			"core/stingray_renderer/shader_libraries/lighting_common#lighting",
			"core/stingray_renderer/shader_libraries/lighting_common#clustered_shading",
			"core/stingray_renderer/shader_libraries/common#billboard_transformation"]

		instance_data = {
			"on_renderer(D3D11) && !defined(MATERIAL_TRANSFER) && defined(INSTANCED)": {
				world = { type = "matrix4x4" }
				"defined(MOTION_BLUR)": {
					last_world = { type = "matrix4x4" }
				}
			}
		}

		stage_conditions = {
			tessellation_control = "defined(DX11_DISPLACEMENT_MAPPING)"
		}

		samplers = {
			"defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": {
			lightmap = {
				sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
				source = "resource_set"
				slot_name = "lightmap"
				type = "2d"
			}
			lightmap_ambient_term = {
				sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
				source = "resource_set"
				slot_name = "lightmap_ambient_term"
				type = "2d"
			}
			lightmap_directional_term = {
				sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
				source = "resource_set"
				slot_name = "lightmap_directional_term"
				type = "2d"
			}
			lightmap_dominant_direction = {
				sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
				source = "resource_set"
				slot_name = "lightmap_dominant_direction"
				type = "2d"
			}
			}

			"defined(HAS_REFRACTION)": {
				hdr0 = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
					source = "resource_set"
					slot_name = "hdr0"
					type = "2d"
				}
			}
			"defined(HAS_REFRACTION) || defined(NEEDS_LINEAR_DEPTH)": {
				linear_depth = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point"
					source = "resource_set"
					slot_name = "linear_depth"
					type = "2d"
				}
			}

			"defined(CALCULATE_LIGHTING)": {
				sun_shadow_map = {
					sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map"
					slot_name = "sun_shadow_map"
					type = "2d"
				}
				local_lights_shadow_atlas = {
					sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map"
					slot_name = "local_lights_shadow_atlas"
					type = "2d"
				}
				//cs_cluster_buffer = {
				//	sampler_state = "clamp_point_no_mip"
				//	slot_name = "cs_cluster_buffer"
				//	type = "2d"
				//}
				//cs_light_index_buffer = {
				//	sampler_state = "clamp_point_no_mip"
				//	slot_name = "cs_light_index_buffer"
				//	type = "2d"
				//}
				//cs_light_data_buffer = {
				//	sampler_state = "clamp_point_no_mip"
				//	slot_name = "cs_light_data_buffer"
				//	type = "2d"
				//}
				//cs_light_shadow_matrices_buffer = {
				//	sampler_state = "clamp_point_no_mip"
				//	slot_name = "cs_light_shadow_matrices_buffer"
				//	type = "2d"
				//}
				static_sun_shadow_map = {
					sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map"
					slot_name = "static_sun_shadow_map"
					type = "2d"
				}
				global_diffuse_map = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
					source = "resource_set"
					slot_name = "global_diffuse_map"
					type = "cube"
				}
				global_specular_map = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
					source = "resource_set"
					slot_name = "global_specular_map"
					type = "cube"
				}
				brdf_lut = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
					source = "resource_set"
					slot_name = "brdf_lut"
					type = "2d"
				}
			}

			"!defined(LOW_RES)": {
				"defined(LOW_RES_ENABLED)": {
					hdr_transparent_div4 = {
						sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
						source = "resource_set"
						slot_name = "hdr_transparent_div4"
						type = "2d"
					}

					hdr_linear_depth_div4 = {
						sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
						source = "resource_set"
						slot_name = "hdr_linear_depth_div4"
						type = "2d"
					}
				}
			}
			
			// TODO: this is incorrect, we need to actually sample the shadow maps...
			/*
			"defined(NEEDS_SUN_SHADOW_MASK) && defined(EMISSIVE_PASS)": {
				ldr0 = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point"
					source = "resource_set"
					slot_name = "ldr0"
					type = "2d"
				}
			}
			*/
		}

		code = {
			glsl =  """
				#if defined(NEEDS_WORLD_SPACE_POSITION) || defined(NEEDS_TANGENT_SPACE) || defined(NEEDS_WORLD_POSE) || defined(NEEDS_WORLD_SPACE_NORMAL)
					#define NEEDS_WORLD
				#endif

				#if defined(NEEDS_WORLD_SPACE_POSITION)
					#define NEEDS_VIEW_PROJECTION
				#endif

				CBUFFER_START(c_per_object)
					UNIFORM mat4 world_view_proj;

					#if defined(NEEDS_WORLD)
						UNIFORM mat4 world;
					#endif

					#if defined(NEEDS_VIEW_PROJECTION)
						UNIFORM mat4 view_proj;
					#endif

					#if defined(NEEDS_INVERSE_WORLD_POSE)
						UNIFORM mat4 inv_world;
					#endif

					#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
						#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
							UNIFORM vec2 lightmap_ambient_term_uv_scale;
							UNIFORM vec2 lightmap_ambient_term_uv_offset;
						#else
							UNIFORM vec2 lightmap_uv_scale;
							UNIFORM vec2 lightmap_uv_offset;
						#endif
					#endif
				CBUFFER_END

				#if defined(CALCULATE_LIGHTING)
					DECLARE_CLUSTER_DATA(cs_cluster_buffer);
					DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer);
					DECLARE_LIGHT_DATA(cs_light_data_buffer);
					DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer);

					DECLARE_COMPARISON_SAMPLER_2D(sun_shadow_map);
					DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas);
					DECLARE_SAMPLER_CUBE(global_specular_map);
					DECLARE_SAMPLER_2D(brdf_lut);
				#endif
				#if defined(TRANSPARENT) || defined(TRANSPARENT_FADE)
					#if defined(HAS_REFRACTION) || defined(NEEDS_LINEAR_DEPTH)
						DECLARE_SAMPLER_2D(hdr0);
						uniform highp usampler2D linear_depth;
						#define HAS_LINEAR_DEPTH
					#endif
				#endif

				#if defined(STAGE_VERTEX)
					layout(location = POSITION0) in vec4 in_position;

					void main() {
						GraphManualChannels params;
						GraphResults graph;

						#if defined(SKINNED)
							vec4 p = vec4(skin_point(in_position, blendindices, blendweights), 1);
							mediump vec3 n = skin_vector(GRAPH_DATA(vertex_normal).xyz, blendindices, blendweights);
							#if defined(NEEDS_TANGENT_SPACE)
								mediump vec3 t = skin_vector(GRAPH_DATA(vertex_tangent).xyz, blendindices, blendweights);
								mediump vec3 b = skin_vector(GRAPH_DATA(vertex_binormal).xyz, blendindices, blendweights);
							#endif
						#else
							vec4 p = in_position;
							mediump vec3 n = GRAPH_DATA(vertex_normal);
							#if defined(NEEDS_TANGENT_SPACE)
								mediump vec3 t = GRAPH_DATA(vertex_tangent);
								mediump vec3 b = GRAPH_DATA(vertex_binormal);
							#endif
						#endif

						#if defined(NEEDS_WORLD_SPACE_NORMAL)
							GRAPH_PARAM(params, world_space_normal) = n * mat3(world);
						#endif

						GRAPH_PARAM(params, vertex_position) = p;

						#if defined(NEEDS_WORLD_SPACE_POSITION)
							vec4 wp = p * world;
							#if defined(NEEDS_WORLD_POS_CHANNEL)
								GRAPH_PARAM(params, world_pos) = wp.xyz;
							#endif
						#endif

						#if defined(NEEDS_TANGENT_SPACE)
							tspace_transform_transpose(
								GRAPH_PARAM(params, tsm0),
								GRAPH_PARAM(params, tsm1),
								GRAPH_PARAM(params, tsm2),
								t, b, n,
								mat3(world));
						#endif

						#if defined(NEEDS_EYE_VECTOR)
							GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb;
						#endif

						#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
							mediump vec2 light_uv_in = GRAPH_DATA(lightmap_uv);

							#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
								mediump vec2 luv = light_uv_in*lightmap_ambient_term_uv_scale + lightmap_ambient_term_uv_offset;
							#else
								mediump vec2 luv = light_uv_in*lightmap_uv_scale + lightmap_uv_offset;
							#endif

							GRAPH_PARAM(params, lightmap_uv) = luv;
						#endif

						#if defined(NEEDS_SCREEN_SPACE_NORMAL)
							GRAPH_PARAM(params, screen_space_normal) = mul(n.xyz, mat3(world_view_proj));
						#endif

						graph_evaluate(graph, params);

						#if defined(NEEDS_WORLD_SPACE_POSITION)
							#if defined(HAS_VERTEX_OFFSET)
								wp.xyz += graph.vertex_offset;
								#if defined(NEEDS_WORLD_POS_CHANNEL)
									GRAPH_PARAM(params, world_pos) = wp.xyz;
								#endif
							#endif
							gl_Position = wp * view_proj;
						#else
							gl_Position = p * world_view_proj;
						#endif
					}
				#elif defined(STAGE_FRAGMENT)
					#if defined(EMISSIVE_PASS)
						layout(location = 0) out mediump vec4 out_color;

						void main() {
							GraphManualChannels params;
							GraphResults graph;
							graph_evaluate(graph, params);

							#if defined(EMISSIVE_PASS)
								#if defined(HAS_OPACITY)
									#if defined(HAS_OPACITY_THRESHOLD)
										float threshold = graph.opacity_threshold;
									#else
										float threshold = 0.5f;
									#endif
									if (graph.opacity < threshold)
										discard;
								#endif
								out_color = vec4(graph.emissive, 0);
							#endif
						}
					#else
						#if defined(TRANSPARENT) || defined(TRANSPARENT_FADE)
							layout(location = 0) out mediump vec4 out_color;
						#else
							GBUFFER_OUTPUT;
						#endif

						#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
							#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
								DECLARE_SAMPLER_2D(lightmap_ambient_term);
								DECLARE_SAMPLER_2D(lightmap_directional_term);
								DECLARE_SAMPLER_2D(lightmap_dominant_direction);
							#else
								DECLARE_SAMPLER_2D(lightmap);
							#endif
						#endif

						DECLARE_SAMPLER_CUBE(global_diffuse_map);

						void main() {
							GraphManualChannels params;
							GraphResults graph;

							#if defined(TRANSPARENT) || defined(TRANSPARENT_FADE)
								float3 world_pos = GRAPH_DATA(world_pos);
								float3 view_dir = float3(camera_world[0].w, camera_world[1].w, camera_world[2].w) - world_pos;
								float3 camera_dir = float3(camera_world[0].y, camera_world[1].y, camera_world[2].y);

								float depth = dot(-view_dir, camera_dir);

								#if defined(NEEDS_PIXEL_DEPTH)
									GRAPH_PARAM(params, pixel_depth) = depth;
									#define HAS_PIXEL_DEPTH
								#endif

								#if defined(NEEDS_SCREEN_POS) || defined(HAS_REFRACTION)
									float2 screen_pos = gl_FragCoord.xy/back_buffer_size;

									#if defined(NEEDS_SCREEN_POS)
										GRAPH_PARAM(params, screen_pos) = screen_pos;
									#endif
								#endif
							#endif

							graph_evaluate(graph, params);

							#if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE)
								#if defined(HAS_OPACITY_THRESHOLD)
									float threshold = graph.opacity_threshold;
								#else
									float threshold = 0.5;
								#endif
								if (graph.opacity < threshold)
									discard;
							#endif

							// Base color
							lowp vec3 base_color = vec3(0);
							#if defined(HAS_BASE_COLOR)
								base_color = graph.base_color;
							#else
								base_color = vec3(0.5, 0.5, 0.5);
							#endif

							// World space normal
							#if defined(HAS_NORMAL)
								#if defined(WORLD_SPACE_NORMAL)
									mediump vec3 wn = normalize(graph.normal);
									#if defined(CULL_NONE) || defined(CULL_FRONT)
										wn = !gl_FrontFacing ? wn : -wn;
									#endif
								#else
									float3 tsm0 = GRAPH_DATA(tsm0).xyz;
									float3 tsm1 = GRAPH_DATA(tsm1).xyz;
									float3 tsm2 = GRAPH_DATA(tsm2).xyz;
									#if defined(CULL_NONE) || defined(CULL_FRONT)
										if (gl_FrontFacing) {
											tsm0.z = -tsm0.z;
											tsm1.z = -tsm1.z;
											tsm2.z = -tsm2.z;
										}
									#endif
									mediump vec3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2);
								#endif
							#else
								mediump vec3 wn = normalize(GRAPH_DATA(world_space_normal));
								#if defined(CULL_NONE) || defined(CULL_FRONT)
									wn = !gl_FrontFacing ? wn : -wn;
								#endif
							#endif

							// Metallic
							lowp float metallic = 0.0;
							#if defined(HAS_METALLIC)
								metallic = graph.metallic;
							#else
								metallic = 0.0;
							#endif

							// Roughness
							half roughness = 0.0;
							#if defined(HAS_ROUGHNESS)
								roughness = max(graph.roughness, 1.f / 255.f);
							#else
								roughness = 0.5;
							#endif

							// Ambient Diffuse
							mediump vec3 ambient = vec3(0.0, 0.0, 0.0);
							#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
								#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
									ambient = TEX2D(lightmap_ambient_term, GRAPH_DATA(lightmap_uv)).rgb;
									mediump vec3 dd = TEX2D(lightmap_dominant_direction, GRAPH_DATA(lightmap_uv)).rgb * 2.0 - vec3(1.0);
									ambient += TEX2D(lightmap_directional_term, GRAPH_DATA(lightmap_uv)).rgb * max(0.0, dot(dd, wn));
								#else
									ambient = TEX2D(lightmap, GRAPH_DATA(lightmap_uv)).rgb;
								#endif
							#else
								ambient = gbuffer_decode_ambient_diffuse_light(TEXCUBELOD(global_diffuse_map, wn, 0.0));
							#endif

							lowp float ambient_occlusion = 1.0;
							#if defined(HAS_AMBIENT_OCCLUSION)
								ambient_occlusion = graph.ambient_occlusion;
							#endif

							// Density
							half density = 1.0;
							#if defined(HAS_DENSITY)
								density = graph.density;
							#endif
							#if !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE)
								out_base_color = gbuffer_encode_base_color(base_color);
								out_normal = gbuffer_encode_normal(wn);
								out_metallic = gbuffer_encode_metallic_mask(metallic);
								out_roughness = gbuffer_encode_roughness(roughness);
								out_ambient_occlusion = gbuffer_encode_ambient_occlusion(ambient_occlusion);
								out_ambient_diffuse_light = gbuffer_encode_ambient_diffuse_light(ambient);

								out_density = gbuffer_encode_density(density);
							#else
								#if defined(HAS_OPACITY)
									half opacity = graph.opacity;
								#else
									half opacity = 0.5;
								#endif

								float3 V = normalize(view_dir);
								float3 N = normalize(wn);

								// 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition.
								float3 specular_color = lerp(new_half3(0.04, 0.04, 0.04), base_color, metallic);
								float3 diffuse_color = lerp(base_color, new_half3(0.0, 0.0, 0.0), metallic);

								float3 acc_diff = new_half3(0.0, 0.0, 0.0);
								float3 acc_spec = new_half3(0.0, 0.0, 0.0);
								float3 translucency = new_half3(0.0, 0.0, 0.0);
								#if defined(CALCULATE_LIGHTING)
									calculate_lighting(world_pos, depth, sun_shadow_map, brdf_lut, global_specular_map, N, V, roughness, ambient, diffuse_color, specular_color, density, base_color, acc_diff, acc_spec, translucency);
									clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer,local_lights_shadow_atlas, world_pos, V, N, diffuse_color, specular_color, roughness, gl_FragCoord.xy, depth, density, base_color, acc_diff, acc_spec, translucency);
								#endif

								float3 acc_refraction = new_half3(0,0,0);
								#if defined(HAS_REFRACTION)
									#if defined(HAS_NORMAL)
										float3 distortion_normal = normalize( mul(N, mat3(view_proj)) );
									#else
										float3 distortion_normal = GRAPH_DATA(screen_space_normal);
									#endif

									// put refraction value more towards the range of real material IOR values (air=1.0 glass=1.5).
									half ior_air = 1.0;
									half ior_range_bias = 0.1;
									half2 distorted_uv = screen_pos - distortion_normal.xy * (graph.refraction - ior_air) * ior_range_bias;

									// avoid including pixels from objects in front of the refractive object
									highp float refraction_depth = uintBitsToFloat(texture(linear_depth, distorted_uv).r);
									bool depth_ok = depth < refraction_depth;
									distorted_uv = lerp(screen_pos, distorted_uv, vec2(depth_ok, depth_ok));

									acc_refraction = TEX2D(hdr0, distorted_uv).rgb;
									acc_refraction *= (1.0 - opacity);

									#if defined(TRANSPARENT_FADE)
										acc_diff *= opacity_to_use;
										acc_spec *= opacity_to_use;
									#endif	
								#endif

								#if defined(TRANSPARENT)
									float3 accumulated_color = acc_diff * opacity + acc_refraction + acc_spec;
								#else
									float3 accumulated_color = acc_diff + acc_refraction + acc_spec;
								#endif
								accumulated_color += translucency;

								#if defined(HAS_EMISSIVE)
									accumulated_color += graph.emissive;
								#endif

								out_color = apply_fog(new_half4_xyz(accumulated_color, opacity), world_pos, depth);
							#endif
						}
					#endif
				#endif
			"""

			hlsl = """
				// We need to disable instancing for the material transfer context as it doesn't use the world transform.
				#if defined(INSTANCED) && defined(MATERIAL_TRANSFER)
					#undef INSTANCED
				#endif
				
				#if defined(CALCULATE_LIGHTING) || defined(NEEDS_PIXEL_DEPTH) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED))
					#define PS_NEEDS_WP
				#endif

				#if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11)) && defined(INSTANCED)) || defined(BILLBOARD)
					#define NEEDS_WORLD_SPACE_POSITION
				#endif

				#if (defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(HAS_ANISOTROPY)
					#define NEEDS_TANGENT_SPACE
				#endif

				#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
					#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
						DECLARE_SAMPLER_2D(lightmap_ambient_term);
						DECLARE_SAMPLER_2D(lightmap_directional_term);
						DECLARE_SAMPLER_2D(lightmap_dominant_direction);
					#else
						DECLARE_SAMPLER_2D(lightmap);
					#endif
				#endif

				#if defined(NEEDS_INVERSE_WORLD_POSE)
					#define NEEDS_INV_WORLD;
				#endif

				#if defined(NEEDS_SUN_SHADOW_MASK) && defined(EMISSIVE_PASS)
					DECLARE_SAMPLER_2D(ldr0);
				#endif

				struct VS_INPUT
				{
					float4 position : POSITION;
					SKIN_INPUT
					GRAPH_VERTEX_INPUT
				};
				
				struct PS_INPUT
				{
					float4 position : SV_POSITION;
					#if defined(PS_NEEDS_WP)
						float3 world_pos : TEXCOORD15;
					#endif
					GRAPH_PIXEL_INPUT
				};

				CBUFFER_START(c_per_object)
						float4x4 view_proj;
						float4x4 world_view_proj;
					#if defined(NEEDS_INV_WORLD)
						float4x4 inv_world;
					#endif
					float4x4 world;
					float4x4 last_world;

					#if defined(JITTER_TRANSPARENCY)
						float inv_jitter_alpha;
					#endif

					#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
						#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
							float2 lightmap_ambient_term_uv_scale;
							float2 lightmap_ambient_term_uv_offset;
						#else
							float2 lightmap_uv_scale;
							float2 lightmap_uv_offset;
						#endif
					#endif

					#if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD)
						#if defined(SECONDARY_SUN_DIRECTION)
							float3 secondary_sun_direction;
							#define billboard_direction secondary_sun_direction
						#else
							float3 sun_direction;
							#define billboard_direction sun_direction
						#endif
					#endif

					#if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER)
						float global_roughness_multiplier;
					#endif

					GRAPH_MATERIAL_EXPORTS
				CBUFFER_END

				#if defined(CALCULATE_LIGHTING)
					DECLARE_CLUSTER_DATA(cs_cluster_buffer);
					DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer);
					DECLARE_LIGHT_DATA(cs_light_data_buffer);
					DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer);

					DECLARE_COMPARISON_SAMPLER_2D(sun_shadow_map);
					DECLARE_COMPARISON_SAMPLER_2D_SHARED(static_sun_shadow_map, sun_shadow_map);
					DECLARE_COMPARISON_SAMPLER_2D_SHARED(local_lights_shadow_atlas, sun_shadow_map);
					DECLARE_SAMPLER_CUBE(global_specular_map);
					DECLARE_SAMPLER_CUBE_SHARED(global_diffuse_map, global_specular_map);
					DECLARE_SAMPLER_2D_SHARED(brdf_lut, global_specular_map);
				#endif

				#if defined(TRANSPARENT) || defined(TRANSPARENT_FADE)
					#if defined(HAS_REFRACTION)
						DECLARE_SAMPLER_2D_SHARED(hdr0, global_specular_map);
					#endif
					#if defined(HAS_REFRACTION) || defined(NEEDS_LINEAR_DEPTH)
						DECLARE_SAMPLER_2D(linear_depth);
						#define HAS_LINEAR_DEPTH
					#endif
				#endif

				#if !defined(LOW_RES) && defined(LOW_RES_ENABLED)
					DECLARE_SAMPLER_2D_SHARED(hdr_transparent_div4, global_specular_map);
					DECLARE_SAMPLER_2D_SHARED(hdr_linear_depth_div4, global_specular_map);
				#endif

				#if defined(INSTANCED) && (defined(RENDERER_D3D11))
					Buffer<float4> idata;
					float ioffset;
				#endif

				inline float3 calc_clip_position_with_halton_offset(float3 wp, float4x4 view_proj)
				{					
					float4 clip_pos = mul(float4(wp, 1), view_proj);
					float4 view_space = clip_pos / clip_pos.w;
					view_space.xy += get_vs_halton_offset(frame_number);
					view_space.xy = view_space.xy * 0.5 + 0.5;
					view_space.y = 1.0 - view_space.y;
					clip_pos = view_space * clip_pos.w;
					return clip_pos.xyw;
				}
				
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_INPUT vs_main(VS_INPUT input
					#if defined(INSTANCED) && (defined(RENDERER_D3D11))
						, uint instance_id : SV_InstanceId
					#endif
					)
				{
					PS_INPUT o;
					float4 p;

					GraphVertexParams params;
					GraphVertexResults results;

					#if defined(INSTANCED) && (defined(RENDERER_D3D11))
						uint offset = (uint)ioffset + instance_id*IDATA_STRIDE;
						world[0] = idata.Load(offset + IDATA_world + 0);
						world[1] = idata.Load(offset + IDATA_world + 1);
						world[2] = idata.Load(offset + IDATA_world + 2);
						world[3] = idata.Load(offset + IDATA_world + 3);

						#if defined(MOTION_BLUR)
							last_world[0] = idata.Load(offset + IDATA_last_world + 0);
							last_world[1] = idata.Load(offset + IDATA_last_world + 1);
							last_world[2] = idata.Load(offset + IDATA_last_world + 2);
							last_world[3] = idata.Load(offset + IDATA_last_world + 3);
						#endif
					#endif

					// Write automatic params
					GRAPH_VERTEX_WRITE_PARAMS(params, input);

					// Write output channels
					#if defined(SKINNED)
						float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1);
						#if defined(MOTION_BLUR)
							float4 last_position = float4(skin_point_last_frame(input.position, input.blendindices, input.blendweights), 1);
						#endif
						#if !defined(BILLBOARD)
							float3 normal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_normal).xyz, input.blendindices, input.blendweights);
							#if defined(NEEDS_TANGENT_SPACE)
								float3 tangent = skin_vector(GRAPH_VERTEX_DATA(input, vertex_tangent).xyz, input.blendindices, input.blendweights);
								float3 binormal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_binormal).xyz, input.blendindices, input.blendweights);
							#endif
						#endif
					#else
						float4 position = input.position;
						#if defined(MOTION_BLUR)
							float4 last_position = position;
						#endif
						#if !defined(BILLBOARD)
							float3 normal = GRAPH_VERTEX_DATA(input, vertex_normal).xyz;
							#if defined(NEEDS_TANGENT_SPACE)
								float3 tangent = GRAPH_VERTEX_DATA(input, vertex_tangent).xyz;
								float3 binormal = GRAPH_VERTEX_DATA(input, vertex_binormal).xyz;
							#endif
						#endif
					#endif

					#if defined(BILLBOARD)
						float4 wp;
						float3 normal, tangent, binormal;
						#if !defined(SKYDOME_BILLBOARD)
							get_billboard_data_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp, normal, tangent, binormal);
						#else
							get_billboard_data_from_direction(billboard_direction, position, wp, normal, tangent, binormal);
						#endif
					#elif defined(NEEDS_WORLD_SPACE_POSITION)
						float4 wp = mul(position, world);
					#endif

					#if defined(NEEDS_UNSKINNED_WORLD_POS)
						float4 unskinned_wp = mul(input.position, world);
						GRAPH_VERTEX_PARAM(params, unskinned_world_pos) = unskinned_wp.xyz;
						GRAPH_VERTEX_PARAM(params, unskinned_normal) = GRAPH_VERTEX_DATA(input, vertex_normal).xyz;
					#endif

					GRAPH_VERTEX_PARAM(params, vertex_position) = position;

					#if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)
						GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = mul(normal, (float3x3)world);
					#endif

					#if defined(NEEDS_EYE_VECTOR)
						#if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD)
							// TODO: not correct length, we can't use length(eye_vector) to determine the distance
							GRAPH_VERTEX_PARAM(params, eye_vector).rgb = billboard_direction;
						#else
							GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb;
						#endif
					#endif

					#if defined(NEEDS_TANGENT_SPACE)
						#if defined(LOCK_NORMAL_ROTATION)
							tspace_transpose(
								GRAPH_VERTEX_PARAM(params, tsm0),
								GRAPH_VERTEX_PARAM(params, tsm1),
								GRAPH_VERTEX_PARAM(params, tsm2),
								tangent, binormal, normal);
						#else
							tspace_transform_transpose(
								GRAPH_VERTEX_PARAM(params, tsm0),
								GRAPH_VERTEX_PARAM(params, tsm1),
								GRAPH_VERTEX_PARAM(params, tsm2),
								tangent, binormal, normal,
								(float3x3)world);
						#endif
					#endif

					// Evaluate all pieces of the graph that should run per-vertex.
					GRAPH_EVALUATE_VERTEX(results, params);
					
					#if defined(NEEDS_WORLD_SPACE_POSITION)
						#if defined(HAS_VERTEX_OFFSET)
							wp += float4(results.vertex_offset, 0);
						#endif
						
						#if defined(HAS_FOV)
							p = mul(wp, camera_custom_fov_view_projection);
						#else
							p = mul(wp, view_proj);
						#endif
					#else
						#if defined(HAS_FOV)
							// TODO: create camera_custom_fov_world_view_projection?
							p = mul(mul(position, world), camera_custom_fov_view_projection); 
						#else
							p = mul(position, world_view_proj);
						#endif
					#endif
					
					#if defined(MOTION_BLUR)
						GRAPH_VERTEX_PARAM(params, last_clip_position) = float3(0.0, 0.0, 0.0);
						#if defined(NEEDS_WORLD_SPACE_POSITION)
							float4 last_wp = mul(last_position, last_world);
							
							#if defined(HAS_VERTEX_OFFSET)
								// TODO: Add _last_ position offset here to support vertex animation.
								// The way it works now will only yield correct results of the offset is constant
								last_wp += float4(results.vertex_offset, 0);
							#endif
						#else
							float4 last_wp = mul(last_position, last_world);
						#endif
						#if defined(HAS_FOV)
							float4 last_clip_pos = mul(last_wp, camera_custom_fov_last_view_projection);
						#else
							float4 last_clip_pos = mul(last_wp, camera_last_view_projection);
						#endif
						float4 last_view_space = last_clip_pos / last_clip_pos.w;
						last_view_space.xy += get_vs_halton_offset(frame_number);
						last_view_space.xy = last_view_space.xy * 0.5 + 0.5;
						last_view_space.y = 1.0 - last_view_space.y;
						last_clip_pos = last_view_space * last_clip_pos.w;
						GRAPH_VERTEX_PARAM(params, last_clip_position) = last_clip_pos.xyw;
					#endif

					#if defined(MATERIAL_TRANSFER)
						float2 unwrapped_uv = GRAPH_VERTEX_DATA(input, lightmap_uv);
						float2 ndc = float2(unwrapped_uv.x, unwrapped_uv.y) * 2 - 1;
						ndc.y *= -1;
						p = float4(ndc, 0, 1);
					#endif

					#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
						GRAPH_VERTEX_PARAM(params, lightmap_uv) = float2(0,0);
						#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
							GRAPH_VERTEX_PARAM(params, lightmap_uv) = GRAPH_VERTEX_DATA(input, lightmap_uv_input)*lightmap_ambient_term_uv_scale + lightmap_ambient_term_uv_offset;
						#else
							GRAPH_VERTEX_PARAM(params, lightmap_uv) = GRAPH_VERTEX_DATA(input, lightmap_uv_input)*lightmap_uv_scale + lightmap_uv_offset;
						#endif
					#endif

					#if defined(PS_NEEDS_WP)
						o.world_pos = wp.xyz;
					#endif
					
					#if defined(MATERIAL_TRANSFER)
						o.position = p;
					#else
						float4 view_space = p / p.w;
						view_space.xy += get_vs_halton_offset(frame_number);
						o.position = view_space * p.w;
					#endif

					// Write results
					GRAPH_VERTEX_WRITE(o, results, params);

					return o;
				}

				#if defined(MATERIAL_TRANSFER)
					struct MATERIAL_TRANSFER_OUT {
						float4 albedo_op : SV_TARGET0;
						float4 emissive : SV_TARGET1;
					};
					
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					MATERIAL_TRANSFER_OUT ps_main(PS_INPUT input) : SV_TARGET0
					{
						GraphPixelParams params;
						GraphPixelResults graph;

						GRAPH_PIXEL_WRITE_PARAMS(params, input);

						#if defined(PS_NEEDS_WP)
							const float3 world_pos = input.world_pos;
							const float3 view_dir = camera_world._m30_m31_m32 - world_pos;
							const float3 camera_dir = camera_world._m10_m11_m12;
							const float depth = dot(-view_dir, camera_dir);
						#endif

						#if defined(NEEDS_PIXEL_DEPTH)
							GRAPH_PIXEL_PARAM(params, pixel_depth) = depth;
						#endif

						#if defined(NEEDS_SCREEN_POS)
							float2 screen_position = (input.position.xy / back_buffer_size);
							GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position;
						#endif
						
						GRAPH_EVALUATE_PIXEL(graph, params);

						MATERIAL_TRANSFER_OUT o;

						float opacity = 1.0;

						#if defined(TRANSPARENT) || defined(TRANSPARENT_FADE)
							#if defined(HAS_OPACITY)
								opacity = graph.opacity;
							#else
								opacity = 0.5;
							#endif
						#elif defined(HAS_OPACITY)
							#if defined(HAS_OPACITY_THRESHOLD)
								float threshold = graph.opacity_threshold;
							#else
								float threshold = 0.5f;
							#endif
							if (graph.opacity < threshold)
								opacity = 0;
							else
								opacity = 1.0;
						#endif

						#if defined(HAS_BASE_COLOR)
							o.albedo_op = float4(graph.base_color, opacity);
						#else
							o.albedo_op = float4(0.5, 0.5, 0.5, opacity);
						#endif

						#if defined(HAS_EMISSIVE)
							o.emissive = float4(graph.emissive, 1.0);
						#else
							o.emissive = float4(0.0, 0.0, 0.0, 0.0);
						#endif

						return o;
					}
				#elif defined(EMISSIVE_PASS)
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
					{
						#if defined(JITTER_TRANSPARENCY)
							uint2 pos = uint2(input.position.xy);
							float alpha = (float)((pos.x%2u) + 2u*(pos.y%2u)) * 0.25;
                       		if (alpha >= 1.0 - inv_jitter_alpha)
                            	discard;
                        #endif

						GraphPixelParams params;
						GraphPixelResults graph;

						GRAPH_PIXEL_WRITE_PARAMS(params, input);

						#if defined(PS_NEEDS_WP)
							const float3 world_pos = input.world_pos;
							const float3 view_dir = camera_world._m30_m31_m32 - world_pos;
							const float3 camera_dir = camera_world._m10_m11_m12;

							const float depth = dot(-view_dir, camera_dir);
						#endif

						#if defined(NEEDS_PIXEL_DEPTH)
							GRAPH_PIXEL_PARAM(params, pixel_depth) = depth;
						#endif

						#if defined(NEEDS_SCREEN_POS)
							float2 screen_position = (input.position.xy / back_buffer_size);
							GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position;
						#endif

						#if defined(NEEDS_SUN_SHADOW_MASK)
							GRAPH_PIXEL_PARAM(params, sun_shadow_mask) = ldr0.tex.Load(int3(input.position.xy, 0)).r;
							#define HAS_SUN_SHADOW_MASK
						#endif

						GRAPH_EVALUATE_PIXEL(graph, params);

						#if defined(EMISSIVE_PASS)
							#if defined(HAS_OPACITY)
								#if defined(HAS_OPACITY_THRESHOLD)
									float threshold = graph.opacity_threshold;
								#else
									float threshold = 0.5f;
								#endif
								if (graph.opacity < threshold)
									discard;
							#endif
							return float4(graph.emissive, 0);
						#endif
					}
				#else
					#if defined(CALCULATE_LIGHTING)
						float4 apply_fog_data(float4 color, float4 fog_data) {
							return float4(color.rgb * (1.0 - fog_data.a) + fog_data.rgb * fog_data.a, color.a);
						}

						struct PS_OUTPUT {
							half4 color : SV_TARGET0;
							#if defined(MOTION_BLUR)
								half2 buffer4 : SV_TARGET1;
							#endif
						};
						DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
						PS_OUTPUT ps_main(PS_INPUT input
					#else
						DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
						GBUFFER_OUT ps_main(PS_INPUT input
					#endif
						#if defined(CULL_NONE) || defined(CULL_FRONT)
							#if defined(GNM)
								, bool vface : S_FRONT_FACE
							#else
								, float vface : VFACE
							#endif
						#endif
						)
					{
						#if defined(CALCULATE_LIGHTING)
							PS_OUTPUT o;
						#else
							GBUFFER_OUT o;
						#endif

						#if defined(JITTER_TRANSPARENCY) && !(defined(TRANSPARENT) || defined(TRANSPARENT_FADE))
							uint2 pos = uint2(input.position.xy);
							float alpha = (float)((pos.x%2u) + 2u*(pos.y%2u)) * 0.25;
                       		if (alpha >= 1.0 - inv_jitter_alpha)
                            	discard;
                        #endif

						GraphPixelParams params;
						GraphPixelResults graph;

						GRAPH_PIXEL_WRITE_PARAMS(params, input);

						#if defined(PS_NEEDS_WP)
							const float3 world_pos = input.world_pos;
							const float3 view_dir = camera_world._m30_m31_m32 - world_pos;
							const float3 camera_dir = camera_world._m10_m11_m12;

							const float depth = dot(-view_dir, camera_dir);
						#endif

						#if defined(NEEDS_PIXEL_DEPTH)
							GRAPH_PIXEL_PARAM(params, pixel_depth) = depth;
							#define HAS_PIXEL_DEPTH 
						#endif

						#if defined(NEEDS_SCREEN_POS) || defined(HAS_REFRACTION) || defined(CALCULATE_LIGHTING)
							float2 screen_pos = (input.position.xy / back_buffer_size);

							#if defined(NEEDS_SCREEN_POS)
								GRAPH_PIXEL_PARAM(params, screen_pos) = screen_pos;
							#endif
						#endif

						GRAPH_EVALUATE_PIXEL(graph, params);

						#if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE)
							#if defined(HAS_OPACITY_THRESHOLD)
								float threshold = graph.opacity_threshold;
							#else
								float threshold = 0.5f;
							#endif
							if (graph.opacity < threshold)
								discard;
						#endif


						// Base color
						float3 base_color_to_use = float3(0,0,0);
						#if defined(HAS_BASE_COLOR)
							base_color_to_use = graph.base_color;
						#else
							base_color_to_use = float3(0.5, 0.5, 0.5);
						#endif

						// World space normal
						#if defined(HAS_NORMAL)
							#if !defined(WORLD_SPACE_NORMAL)
								float3 tsm0 = GRAPH_PIXEL_DATA(input, tsm0).xyz;
								float3 tsm1 = GRAPH_PIXEL_DATA(input, tsm1).xyz;
								float3 tsm2 = GRAPH_PIXEL_DATA(input, tsm2).xyz;
								#if (defined(CULL_NONE) || defined(CULL_FRONT)) && !defined(NORMAL_FLIP_DISABLED)
									if (!front_facing(vface)) {
										tsm0.z = -tsm0.z;
										tsm1.z = -tsm1.z;
										tsm2.z = -tsm2.z;
									}
								#endif
								float3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2);
								#if defined(LOCK_NORMAL_ROTATION)
									wn = mul(wn, (float3x3)world);
								#endif
							#else
								float3 wn = normalize(graph.normal);
								#if (defined(CULL_NONE) || defined(CULL_FRONT)) && !defined(NORMAL_FLIP_DISABLED)
									wn = !front_facing(vface) ? -wn : wn;
								#endif
							#endif
						#else
							float3 wn = normalize((float3)GRAPH_PIXEL_DATA(input, world_space_normal).rgb);
							#if (defined(CULL_NONE) || defined(CULL_FRONT)) && !defined(NORMAL_FLIP_DISABLED)
								wn = !front_facing(vface) ? -wn : wn;
							#endif
						#endif

						// Metallic
						half metallic_ = 0.f;
						#if defined(HAS_METALLIC)
							metallic_ = graph.metallic;
						#else
							metallic_ = 0.f;
						#endif

						// Roughness
						half roughness_ = 0.f;
						#if defined(HAS_ROUGHNESS)
							roughness_ = graph.roughness;
						#else
							roughness_ = 0.5;
						#endif
						#if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER)
							roughness_ *= global_roughness_multiplier;
						#endif

						// Velocity vector
							#if defined(MOTION_BLUR)
								float3 last_clip_pos = GRAPH_PIXEL_DATA(input, last_clip_position);
							float2 current_screen_pos = (input.position.xy / back_buffer_size - viewport.zw) / viewport.xy;
								float2 last_screen_pos = last_clip_pos.xy / last_clip_pos.z;
							VELOCITY(o) = encode_velocity(viewport.xy*(current_screen_pos - last_screen_pos));
							#endif

						#if defined(CALCULATE_LIGHTING)
							// Ambient Diffuse
							float3 ambient = float3(0, 0, 0);
							#if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING)
								ambient = GRAPH_PIXEL_DATA(input, baked_light).rgb;
							#elif defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
								#if defined(HAS_DIRECTIONAL_LIGHTMAPS)
									ambient = TEX2D(lightmap_ambient_term, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb;
									float3 dd = TEX2D(lightmap_dominant_direction, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb * 2 - 1;
									ambient += TEX2D(lightmap_directional_term, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb * max(0, dot(dd, wn));
								#else
									ambient = TEX2D(lightmap, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb;
								#endif
							#else
								ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0));
							#endif
					
							// ensure that lightmap debug grid is visible in dark areas of a level
							#if defined(DRAW_LIGHTMAP_TEXELS) && defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
								ambient += float3(0.1, 0.1, 0.1);
							#endif
						#endif

						// Density
						half density_ = 1.0f;
						#if defined(HAS_SKIN_SSS_STRENGTH) && (!defined(TRANSPARENT) && !defined(TRANSPARENT_FADE))
							density_ = lerp(1.0, graph.sss_strength, skin_material_enabled);
						#elif defined(HAS_DENSITY)
							density_ = graph.density;
						#endif
						#if !defined(CALCULATE_LIGHTING)
							BASE_COLOR(o) = gbuffer_encode_base_color(base_color_to_use);
							NORMAL(o) = gbuffer_encode_normal(wn);
							METALLIC(o) = gbuffer_encode_metallic_mask(metallic_);
							ROUGHNESS(o) = gbuffer_encode_roughness(roughness_);
							// AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(ambient);
							
							#if defined(HAS_AMBIENT_OCCLUSION)
								AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(graph.ambient_occlusion);
							#else
								AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f);
							#endif
							DENSITY(o) = gbuffer_encode_density(density_);
						#else
							#if defined(HAS_OPACITY)
								half opacity_to_use = graph.opacity;
							#else
								half opacity_to_use = 0.5;
							#endif


							const float3 V = normalize(view_dir);
							const float3 N = wn;

							#if defined(CALCULATE_LIGHTING) && defined(HAS_ANISOTROPY)
								float anisotropy_ = graph.anisotropy;

								// TODO: Reuse and use the real tangent and binormal. Optional should be to input a tangent/binormal map.
								float3 tsm00 = GRAPH_PIXEL_DATA(input, tsm0).xyz;
								float3 tsm11 = GRAPH_PIXEL_DATA(input, tsm1).xyz;
								float3 tsm22 = GRAPH_PIXEL_DATA(input, tsm2).xyz;

								// create tangentspace vectors
								#ifdef HAS_NORMAL
									float3 B = normalize(float3(tsm00.y, tsm11.y, tsm22.y));
									float3 T = cross(B, N);
									B = cross(N, T);
								#else
									float3 T = normalize(float3(tsm00.x, tsm11.x, tsm22.x));
									float3 B = normalize(float3(tsm00.y, tsm11.y, tsm22.y));
								#endif
							#endif
							
							// 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition.
							const float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color_to_use, metallic_);
							float3 diffuse_color = lerp(base_color_to_use, new_half3(0,0,0), metallic_);

							float3 acc_diff = 0;
							float3 acc_spec = 0;
							float3 translucency = 0;
							#if defined(CALCULATE_LIGHTING)
								#if defined(D3D11)
									// If we are currently capturing a reflection probe, use specular F0 as diffuse color for metallics
									diffuse_color = (capture_cubemap == 1) ? lerp(diffuse_color, specular_color, metallic_) : diffuse_color;
								#endif

								calculate_lighting(world_pos, depth, sun_shadow_map, static_sun_shadow_map, brdf_lut, global_specular_map, N, V, roughness_, ambient, diffuse_color, specular_color, density_, base_color_to_use, acc_diff, acc_spec, translucency);
								clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer, local_lights_shadow_atlas, world_pos, V, N, diffuse_color, specular_color, roughness_, input.position.xy, input.position.w, density_, base_color_to_use, acc_diff, acc_spec, translucency);
							#endif

							float3 acc_refraction = float3(0,0,0);
							#if defined(HAS_REFRACTION)
								#if defined(HAS_NORMAL)
									float3 distortion_normal = normalize( mul(N, (float3x3)view_proj) );
								#else
									float3 distortion_normal = GRAPH_CHANNEL(input, screen_space_normal);
								#endif

								// A negative screen-space normal (-y) means we want to sample more towards the bottom of the screen, so we need to flip this value, because our screen_pos uses 0,0 for top left corner
								distortion_normal.y = 1-distortion_normal.y;

								// put refraction value more towards the range of real material IOR values (air=1.0 glass=1.5).
								half ior_air = 1.0;
								half ior_range_bias = 0.1;
								half2 distorted_uv = screen_pos - distortion_normal.xy * (graph.refraction - ior_air) * ior_range_bias;

								// avoid including pixels from objects in front of the refractive object
								float refraction_depth = TEX2D(linear_depth, distorted_uv).r;
								bool depth_ok = depth < refraction_depth;
								distorted_uv = lerp(screen_pos, distorted_uv, depth_ok);

								acc_refraction = TEX2D(hdr0, distorted_uv).rgb;
								acc_refraction *= (1 - opacity_to_use);
							#endif

							#if !defined(LOW_RES) && defined(LOW_RES_ENABLED)
								float2 screen_uv = input.position.xy / back_buffer_size;

								// Fade out high resolution particle if the low resolution particles is infront of the high resolution particle
								float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg;
								float depth_mean = particle_depth_values.x;
								float depth_variance = particle_depth_values.y - depth_mean*depth_mean;
								float background_depth = depth;
								
								float diff = max(background_depth - depth_mean, 0.0);
								float C = 1.38629436112; // 2 * ln(2)
								float T = exp2(-diff*diff / (C * max(depth_variance, 0.001)));
								float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T);
								opacity_to_use *= 1.0 - alpha_modifier;
							#endif
							
							#if defined(JITTER_TRANSPARENCY)
								opacity_to_use *= 1.0 - inv_jitter_alpha;
	                        #endif
							
							float3 accumulated_color = acc_diff + translucency;
							#if defined(HAS_EMISSIVE)
								accumulated_color += graph.emissive;
							#endif
							#if defined(TRANSPARENT)
								// Transparent don't apply opacity on specular reflection or refraction.
								float4 fog_data = calc_fog_vs(world_pos, depth);
								float4 color = apply_fog_data(half4(accumulated_color, opacity_to_use), fog_data);
								color.rgb = color.rgb * color.a + acc_spec * (1.0 - fog_data.a);
							#else
								accumulated_color += acc_spec;
								float4 color = apply_fog(half4(accumulated_color, opacity_to_use), world_pos, depth);
								color.rgb = color.rgb * color.a;
							#endif
							color.rgb += acc_refraction;
							o.color = color;					
							#if defined(OPAQUE_FORWARD)
								o.color.a = 1.0; // make sure it is solid
							#endif
						#endif

						return o;
					}
				#endif
			"""
		}
	}

	depth_only = {
		include: [
			"core/stingray_renderer/shader_libraries/common#common",
			"core/stingray_renderer/shader_libraries/common#skinning",
			"core/stingray_renderer/shader_libraries/common#billboard_transformation",
			"core/stingray_renderer/shader_libraries/common#taa_offsets"]

		instance_data = {
			"on_renderer(D3D11) && defined(INSTANCED)": {
				world = { type = "matrix4x4" }

				"defined(DRAW_WIREFRAME)": {
					dev_wireframe_color = { type = "vector4" }
				}
				"defined(DRAW_OUTLINE)": {
					outline_color = { type = "vector4" }
					outline_time = { type = "scalar" }
					"defined(JITTER_TRANSPARENCY)": {
						inv_jitter_alpha = { type = "scalar" }
					}
				}		
			}	
		}

		samplers = {
			"defined(OUTLINE_MASK_READ)": {
				outline_mask = { 
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point"
					source = "resource_set"
					slot_name = "outline_mask"
					type = "2d"
				}
			}
		}

		code = {
			glsl = """
				CBUFFER_START(c_per_object)
					UNIFORM mat4 world_view_proj;
					#if defined(NEEDS_WORLD_SPACE_POSITION) || defined(NEEDS_WORLD_SPACE_NORMAL)
						UNIFORM mat4 world;
						UNIFORM mat4 view_proj;
					#endif
					#if defined(NEEDS_INVERSE_WORLD_POSE)
						UNIFORM mat4 inv_world;
					#endif
				CBUFFER_END

				#if defined(STAGE_VERTEX)
					layout(location = POSITION0) in vec4 in_position;

					void main() {
						GraphManualChannels params;
						GraphResults graph;

						#if defined(NEEDS_WORLD_SPACE_NORMAL)
							GRAPH_PARAM(params, world_space_normal) = GRAPH_DATA(vertex_normal) * mat3(world);
						#endif

						#if defined(SKINNED)
							vec4 op = vec4(skin_point(in_position, blendindices, blendweights), 1);
						#else
							vec4 op = in_position;
						#endif
						GRAPH_PARAM(params, vertex_position) = op;

						#if defined(NEEDS_WORLD_SPACE_POSITION)
							vec4 wp = op * world;
						#endif

						#if defined(NEEDS_EYE_VECTOR)
							GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb;
						#endif

						graph_evaluate(graph, params);

						#if defined(HAS_VERTEX_OFFSET)
							wp.xyz += graph.vertex_offset;
							gl_Position = wp * view_proj;
						#else
							gl_Position = op * world_view_proj;
						#endif
					}
				#elif defined(STAGE_FRAGMENT)
					void main() {
						#if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE)
							GraphManualChannels params;
							GraphResults graph;
							graph_evaluate(graph, params);

							#if defined(HAS_OPACITY_THRESHOLD)
								float threshold = graph.opacity_threshold;
							#else
								float threshold = 0.5f;
							#endif

							if (graph.opacity < threshold)
								discard;
						#endif
					}
				#endif
			"""

			hlsl = """
			
				#if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11)) && defined(INSTANCED)) || defined(BILLBOARD)
					#define NEEDS_WORLD_SPACE_POSITION
				#endif

				struct VS_INPUT {
					float4 position : POSITION;
					SKIN_INPUT
					GRAPH_VERTEX_INPUT
				};

				struct PS_INPUT {
					float4 position : SV_POSITION;
					#if (defined(RENDERER_D3D11)) && defined(INSTANCED) 
						#if defined(DRAW_WIREFRAME)
							float4 instance_wireframe_color : COLOR0;
							#if defined(DRAW_OUTLINE)
								float4 instance_outline_color : COLOR1;
								float instance_outline_time : COLOR2;
								#if defined(JITTER_TRANSPARENCY)
									float instance_inv_jitter_alpha : COLOR3;
								#endif
							#endif
						#elif defined(DRAW_OUTLINE)
							float4 instance_outline_color : COLOR0;
							float instance_outline_time : COLOR1;
							#if defined(JITTER_TRANSPARENCY)
								float instance_inv_jitter_alpha : COLOR2;
							#endif
						#endif
					#endif
					GRAPH_PIXEL_INPUT
				};

				CBUFFER_START(c_depth_only)
					float4x4 world;
					#if defined(NEEDS_WORLD_SPACE_POSITION)
						float4x4 view_proj;
					#else
						float4x4 world_view_proj;
					#endif
					#if defined(NEEDS_INVERSE_WORLD_POSE)
						float4x4 inv_world;
					#endif
					float4 dev_wireframe_color;

					#if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD)
						#if defined(SECONDARY_SUN_DIRECTION)
							float3 secondary_sun_direction;
							#define billboard_direction secondary_sun_direction
						#else
							float3 sun_direction;
							#define billboard_direction sun_direction
						#endif
					#endif

					float4 outline_color;
					float outline_time;
					float inv_jitter_alpha;
					GRAPH_MATERIAL_EXPORTS
				CBUFFER_END

				#if defined(OUTLINE_MASK_READ)
					DECLARE_SAMPLER_2D(outline_mask);
				#endif

				#if defined(INSTANCED) && (defined(RENDERER_D3D11))
					Buffer<float4> idata;
					float ioffset;
				#endif
				
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_INPUT vs_main(VS_INPUT input
					#if defined(INSTANCED) && (defined(RENDERER_D3D11))
						, uint instance_id : SV_InstanceId
					#endif
					)
				{
					PS_INPUT o;
					float4 p;

					GraphVertexParams params;
					GraphVertexResults results;

					#if ((defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE))
						GRAPH_VERTEX_PARAM(params, tsm0) = float3(0, 0, 0);
						GRAPH_VERTEX_PARAM(params, tsm1) = float3(0, 0, 0);
						GRAPH_VERTEX_PARAM(params, tsm2) = float3(0, 0, 0);
					#endif

					#if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)
						GRAPH_VERTEX_PARAM(params, lightmap_uv) = float2(0, 0);
					#endif

					#if defined(INSTANCED) && (defined(RENDERER_D3D11))
						uint offset = (uint)ioffset;
						world[0] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 0));
						world[1] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 1));
						world[2] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 2));
						world[3] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 3));

						#if defined(DRAW_WIREFRAME)
							o.instance_wireframe_color = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_dev_wireframe_color));
						#endif	
						#if defined(DRAW_OUTLINE)
							o.instance_outline_color = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_outline_color));
							o.instance_outline_time = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_outline_time));
							#if defined(JITTER_TRANSPARENCY)
								o.instance_inv_jitter_alpha = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_inv_jitter_alpha));
							#endif
						#endif
					#endif

					// Write automatic params
					GRAPH_VERTEX_WRITE_PARAMS(params, input);

					// Write output channels
					#if defined(SKINNED)
						float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1);
						#if !defined(BILLBOARD) && (!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL))
							float3 normal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_normal).xyz, input.blendindices, input.blendweights);
						#endif
					#else
						float4 position = input.position;
						#if !defined(BILLBOARD) && (!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL))
							float3 normal = GRAPH_VERTEX_DATA(input, vertex_normal).xyz;
						#endif
					#endif

					#if defined(BILLBOARD)
						#if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)
							float4 wp;
							float3 normal, tangent, binormal;
							#if !defined(SKYDOME_BILLBOARD)
								get_billboard_data_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp, normal, tangent, binormal);
							#else
								get_billboard_data_from_direction(billboard_direction, position, wp, normal, tangent, binormal);
							#endif
						#else
							float4 wp;
							#if !defined(SKYDOME_BILLBOARD)
								get_billboard_positions_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp);
							#else
								get_billboard_positions_from_direction(billboard_direction, position, wp);
							#endif
						#endif
					#elif defined(NEEDS_WORLD_SPACE_POSITION)
						float4 wp = mul(position, world);
						// TODO: Expose output channel here
					#endif

					// Write output channels
					GRAPH_VERTEX_PARAM(params, vertex_position) = position;

					// All members of the params struct has to be initialized, so assign dummy normal here.
					#if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)
						GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = mul(normal, (float3x3)world);
					#endif

					#if defined(NEEDS_TANGENT_SPACE)
						GRAPH_VERTEX_PARAM(params, tsm0) =
							GRAPH_VERTEX_PARAM(params, tsm1) =
							GRAPH_VERTEX_PARAM(params, tsm2) =
							float3(0, 0, 0);
					#endif

					#if defined(NEEDS_EYE_VECTOR)
						#if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD)
							// TODO: not correct length, we can't use length(eye_vector) to determine the distance
							GRAPH_VERTEX_PARAM(params, eye_vector).rgb = billboard_direction;
						#else
							GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb;
						#endif
					#endif

					// Evaluate all pieces of the graph that should run per-vertex.
					GRAPH_EVALUATE_VERTEX(results, params);

					// Apply world pos offset
					#if defined(NEEDS_WORLD_SPACE_POSITION)
						#if defined(HAS_VERTEX_OFFSET)
							wp += float4(results.vertex_offset, 0);
						#endif
						p = mul(wp, view_proj);
					#else
						p = mul(position, world_view_proj);
					#endif

					#if defined(DRAW_OUTLINE) || defined(OUTLINE_MASK_READ) || defined(OUTLINE_MASK_WRITE) || defined(HAS_FOV)
						float4 view_space = p / p.w;
						view_space.xy += get_vs_halton_offset(frame_number);
						o.position = view_space * p.w;
					#else
						o.position = p;
					#endif

					// Write results
					GRAPH_VERTEX_WRITE(o, results, params);

					return o;
				}
				
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				#if (((defined(RENDERER_D3D11)) && defined(INSTANCED)) || defined(HAS_OPACITY)) || defined(OUTLINE_MASK_READ)
					float4 ps_main(PS_INPUT input) : SV_TARGET0
				#else
					float4 ps_main() : SV_TARGET0
				#endif

				#if defined(DRAW_WIREFRAME)
					{
						#if (defined(RENDERER_D3D11)) && defined(INSTANCED)
							return input.instance_wireframe_color;
						#else
							return dev_wireframe_color;
						#endif
					}	
				#elif defined(OUTLINE_MASK_WRITE)
					{
						return float4(1,1,1,1);
					}
				#elif defined(OUTLINE_MASK_READ)
					{
						float2 uv = input.position.xy / back_buffer_size;
						float mask = 1.0 - TEX2D(outline_mask, uv).r;
						if( mask < 1.0 )
							discard;

						#if (defined(RENDERER_D3D11)) && defined(INSTANCED)
							return input.instance_outline_color;
						#else
							return outline_color;
						#endif
					}
				#elif defined(DRAW_OUTLINE)
					{
						#if defined(HAS_OPACITY)
							GraphPixelParams params;
							GraphPixelResults graph;
							GRAPH_PIXEL_WRITE_PARAMS(params, input);
							GRAPH_EVALUATE_PIXEL(graph, params);

							#if defined(HAS_OPACITY_THRESHOLD)
								float threshold = graph.opacity_threshold;
							#else
								float threshold = 0.5f;
							#endif

							if (graph.opacity < threshold)
								discard;
						#endif

						#if (defined(RENDERER_D3D11)) && defined(INSTANCED)
							float diff = time - input.instance_outline_time;
							float multiplier = min(diff / 0.25, 1);
							#if defined(JITTER_TRANSPARENCY)
	                            multiplier *= 1.0 - input.instance_inv_jitter_alpha;
	                        #endif
							return input.instance_outline_color * multiplier;
						#else
							float diff = time - outline_time;
							float multiplier = min(diff / 0.25, 1);
							#if defined(JITTER_TRANSPARENCY)
	                            multiplier *= 1.0 - inv_jitter_alpha;
	                        #endif
							return outline_color * multiplier;
						#endif
					}			
				#else
					{
						#if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE)
							GraphPixelParams params;
							GraphPixelResults graph;
							GRAPH_PIXEL_WRITE_PARAMS(params, input);
							GRAPH_EVALUATE_PIXEL(graph, params);

							#if defined(HAS_OPACITY_THRESHOLD)
								float threshold = graph.opacity_threshold;
							#else
								float threshold = 0.5f;
							#endif

							if (graph.opacity < threshold)
								discard;
						#endif

						return float4(1, 1, 1, 1);
					}
				#endif
			"""
		}
	}
}
