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

inputs = {
	"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"
	}

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

	"6ae3afc3-7a2d-4572-a376-f2649241c71d" = {
		is_required = false
		name = "density"
		display_name = "Density"
		type = { scalar: ["HAS_DENSITY"] }
		domain = "pixel"
	}
}

options = {
	"a84e242a-2ee2-4542-846a-3691a583e62c" = "USE_GLOBAL_ROUGHNESS_MULTIPLIER"
}

ui = [
	{ type = "checkbox" display_name = "Use Global Roughness Multiplier" option = "a84e242a-2ee2-4542-846a-3691a583e62c" }
]

render_state = {
	terrain = {
		inherit: ["core/stingray_renderer/shader_libraries/common#gbuffer_material"]
		state: {
			fill_mode = "fill_solid"
			cull_mode = "cull_ccw"
		}
	}


	terrain_alpha_masked_with_prez = {
		inherit: ["core/stingray_renderer/shader_libraries/common#gbuffer_material"]
		state: {
			
			fill_mode = "fill_solid"
			cull_mode = "cull_ccw"
			z_func = "equal"
			z_write_enable = "false"
			z_enable = "true" 
		}
	}

	depth_only = {
		inherit: ["core/stingray_renderer/shader_libraries/common#default"] 
		state: {
			write_mask0 = "0x0"
			write_mask1 = "0x0"
			write_mask2 = "0x0"
			write_mask3 = "0x0"
		}
	}

	terrain_depth_prepass = {
		inherit: ["depth_only"]
		state: {
			fill_mode = "fill_solid"
			cull_mode = "cull_ccw"
		}
	}

	wireframe = {
		inherit: ["core/stingray_renderer/shader_libraries/common#opacity"]
		state: {
			fill_mode = "fill_wireframe"
			"on_renderer(D3D11, D3D12)" = {
				depth_bias = "-1"
				depth_bias_clamp = "-0.00015"
				slope_scale_depth_bias = "-2.0"
			}
		}
	}

	shadow_caster = {
		inherit: ["depth_only"]
		state: {
			"on_renderer(D3D11, D3D12)" = {
				depth_bias = "0xff"
				slope_scale_depth_bias = "1.0"
			}
            "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"
			}
		}
	}
}

sampler_state = { }

channels = 	{
	"defined(NEEDS_TANGENT_SPACE)": {
		tsm0 = { type = "float3" domain = "vertex" }
		tsm1 = { type = "float3" domain = "vertex" }
		tsm2 = { type = "float3" domain = "vertex" }
	}

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

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

	vertex_position = { type = "float4" domain = "vertex" }
	terrain_uv = { type = "float2" domains=["vertex" "pixel"] }
}

permutation_sets = {
}

shader_contexts = {
	shadow_caster = {
		passes_sort_mode = "immediate"
		compile_with = [
			{ if: "on_renderer(D3D11, D3D12, GNM)" }
		]

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

	default = {
		passes_sort_mode = "deferred"
		compile_with = [
			{ if: "on_renderer(D3D11, D3D12, GNM)" permute_with: "default" }
		]

		passes = [
			{ if: "defined(HAS_OPACITY)" then: [  
				{ layer="depth_prepass" code_block="terrain_depth_only" defines=["DEPTH_PREPASS"] render_state="terrain_depth_prepass" } // TODO: Make a depth shader
				{ layer="gbuffer_terrain" code_block="terrain" defines="MOTION_BLUR" render_state="terrain_alpha_masked_with_prez" }
				//{ layer="gbuffer_terrain" code_block="terrain" defines=["MOTION_BLUR" "SINGLE_PASS"] render_state="terrain" }
			] else: [
				{ layer="gbuffer_terrain" code_block="terrain" defines="MOTION_BLUR" render_state="terrain" }
			]}
			{ if: "!on_renderer(GL)" then: [
				{ layer="wireframe" code_block="terrain_depth_only" defines=["DRAW_WIREFRAME"] render_state="wireframe" branch_key="dev_wireframe" }
			]}
		]
	}
}

code_blocks = {
	terrain_shared = {
		code = {
			hlsl = """
				float2 morph(float2 uv, float2 wp, float t, float gsize, float psize) {
					float3 grid_size = { gsize, gsize*0.5, 2.f/gsize };
					float2 frac_part = (frac(uv*grid_size.yy) * grid_size.zz) * psize.xx;
					return wp - frac_part * t;				
				}

				float3 normal_from_hmap(Sampler2D height_map, float2 uv, float2 texel_size, float texel_aspect) {	
					#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
						float4 h = {
							TEX2D(height_map, uv + texel_size * float2(-1, 0)).r,
							TEX2D(height_map, uv + texel_size * float2(1, 0)).r,
							TEX2D(height_map, uv + texel_size * float2(0, -1)).r,
							TEX2D(height_map, uv + texel_size * float2(0, 1)).r
						};
					#endif

					h *= texel_aspect;
					
					float3 n = {
						h[0] - h[1], 
						h[3] - h[2],
						2
					};				
					
					return normalize(n);
				}

				float3 normal_from_hmap(Sampler2D height_map, float4 uv_lod, float2 texel_size, float texel_aspect) {	
					#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
						float4 h = {
							TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(-1, 0), uv_lod.zw).r,
							TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(1,  0), uv_lod.zw).r,
							TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(0, -1), uv_lod.zw).r,
							TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(0,  1), uv_lod.zw).r
						};
					#endif

					h *= texel_aspect;
					
					float3 n = {
						h[0] - h[1], 
						h[3] - h[2],
						2
					};				
					
					return normalize(n);
				}
			"""
		}
	}

	terrain = {
		include:[
			"core/stingray_renderer/shader_libraries/common#common",
			"core/stingray_renderer/shader_libraries/common#gbuffer_access",
			"core/stingray_renderer/shader_libraries/common#taa_offsets",
			"terrain_shared" ]

		samplers = {
			//global_diffuse_map = {
			//	sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
			//	source = "resource_set"
			//	slot_name = "global_diffuse_map"
			//	type = "cube"
			//}
			hmap = {
				sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
				source = "resource_set"
				slot_name = "hmap"
				type = "2d"
			}
		}

		code = {
			hlsl = """
				#if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) 
					#define NEEDS_WORLD_SPACE_POSITION
				#endif

				DECLARE_SAMPLER_2D(hmap);

				struct VS_INPUT {
					float4 position : POSITION;
					GRAPH_VERTEX_INPUT
				};

				struct PS_INPUT {
					float4 position : SV_POSITION;					
					float4 color : COLOR;
					GRAPH_PIXEL_INPUT
				};

				CBUFFER_START(c_per_object)
					float4x4 world;
					float4x4 world_view_proj;
					float3 terrain_size;
					float3 lod_camera_pos;
					float4x4 last_world;

					#if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER)
						float global_roughness_multiplier;
					#endif

					float4 dev_wireframe_color;
					GRAPH_MATERIAL_EXPORTS
				CBUFFER_END

				#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
					Buffer<float4> idata;
				#endif

				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_INPUT vs_main(VS_INPUT input, uint instance_id : SV_InstanceId)
				{
					PS_INPUT o;
					float4 p;

					GraphVertexParams params;
					GraphVertexResults results;

					#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
						float4 pos_scale = idata.Load(instance_id * 2 + 0);
						float4 tile_info = idata.Load(instance_id * 2 + 1);			
					#endif
					float2 half_size = terrain_size.xy * 0.5;
					float2 mip = input.position.xy;				
					float2 pos = (mip.xy - 0.5) * pos_scale.zw + pos_scale.xy;
									
					float3 temp_wp = mul(float4(pos, 0, 1), world);
					float t = 1-saturate((distance(temp_wp, camera_pos) - (tile_info.x - pos_scale.z*0.5)) / tile_info.y);						

					float2 huv = ((pos.xy / half_size) * 0.5 + 0.5);
					#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
						huv.y = 1-huv.y;
					#endif
					float h = TEX2DLOD(hmap, huv, 0).r * terrain_size.z;	

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

					// Write output channels
					float4 position = float4(pos.xy, h, 1);
					#if defined(NEEDS_WORLD_SPACE_POSITION)
						float4 wp = mul(position, world);
						// TODO: Expose output channel here
					#endif
					GRAPH_VERTEX_PARAM(params, vertex_position) = position;

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

					GRAPH_VERTEX_PARAM(params, terrain_uv) = huv;

					#if defined(NEEDS_TANGENT_SPACE)
						#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
							float2 res;
							TEXTURE_NAME(hmap).GetDimensions(res.x, res.y);
							float2 inv_hmap_size = 1.0/res;
						#endif
						float3 normal = normal_from_hmap(hmap, float4(huv, 0, 0), inv_hmap_size, terrain_size.z);

						// create tangentspace vectors
						normal = mul(normal, (float3x3)world);
						float3 tangent = normalize(cross(world._m10_m11_m12, normal)); // TODO: redundant normalize?
						float3 binormal = normalize(cross(normal, tangent)); // TODO: redundant normalize?

						tspace_transpose(
							GRAPH_VERTEX_PARAM(params, tsm0), 
							GRAPH_VERTEX_PARAM(params, tsm1), 
							GRAPH_VERTEX_PARAM(params, tsm2), 
							tangent, binormal, normal);
					#endif

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

					p = mul(position, world_view_proj);

					#if defined(MOTION_BLUR)
						float4 last_wp = mul(position, last_world);
						float4 last_clip_pos = mul(last_wp, camera_last_view_projection);
						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(DRAW_WIREFRAME) 
						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
					
					const float3 lod_cols[8] = {
						float3(1,0,0),
						float3(0,1,0),
						float3(0,0,1),
						float3(1,1,0),
						float3(0,1,1),
						float3(1,1,0.5),
						float3(1,0,0.5),
						float3(0,1,0.5)
					};
					
					o.color = float4(lod_cols[(uint)tile_info.w], t);					

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

					return o;
				}

				#if defined(DRAW_WIREFRAME)
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
					{
						return dev_wireframe_color;
					}
				#elif defined(DEPTH_ONLY)
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
					{
						#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

						return float4(1,1,1,1);
					}				
				#else
					//DECLARE_SAMPLER_CUBE(global_diffuse_map);

					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					GBUFFER_OUT ps_main(PS_INPUT input)
					{
						GBUFFER_OUT o;

						GraphPixelParams params;
						GraphPixelResults graph;

						GRAPH_PIXEL_WRITE_PARAMS(params, input);
						GRAPH_EVALUATE_PIXEL(graph, params);

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

							if (graph.opacity < threshold)
								discard;
						#endif
						
						#if !defined(HAS_NORMAL)
							#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
								float2 res;
								TEXTURE_NAME(hmap).GetDimensions(res.x, res.y);
								float2 inv_hmap_size = 1.0/res;
							#endif
							float3 tnormal = normal_from_hmap(hmap, GRAPH_PIXEL_DATA(input, terrain_uv), inv_hmap_size, terrain_size.z);
						#endif

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

						// World space normal
						#if defined(HAS_NORMAL)
							float3 wn = graph.normal;
						#else
							float3 wn =  tnormal;
						#endif
						wn =  mul(wn, (float3x3)world);

						NORMAL(o) = gbuffer_encode_normal(wn);

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

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

						#if defined(MOTION_BLUR)
							float3 last_clip_pos = GRAPH_PIXEL_DATA(input, last_clip_position);
							float2 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*(screen_pos - last_screen_pos));
						#else
							VELOCITY(o) = encode_velocity(float2(0.0, 0.0));
						#endif

						// Density
						half density = 1.0;
						#if defined(HAS_DENSITY)
							density = graph.density;
						#endif
						DENSITY(o) = density;

						// Since we don't have baked lighting, we don't use this
						// // 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) && defined(HAS_UV_UNWRAP)
						// 	ambient = TEX2D(lightmap, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb;
						// #else
						// 	ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0));
						// #endif

						// 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

						return o;
					}
				#endif
			"""
		}
	}

	terrain_depth_only = {
		include:[
			"core/stingray_renderer/shader_libraries/common#common",
			"core/stingray_renderer/shader_libraries/common#gbuffer_access",
			"core/stingray_renderer/shader_libraries/common#taa_offsets",
			"terrain_shared" ]

		samplers = {
			hmap = {
				sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
				source = "resource_set"
				slot_name = "hmap"
				type = "2d"
			}
		}

		code = {
			hlsl = """
				#if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) 
					#define NEEDS_WORLD_SPACE_POSITION
				#endif

				DECLARE_SAMPLER_2D(hmap);

				struct VS_INPUT {
					float4 position : POSITION;
					#if defined(HAS_OPACITY)
						GRAPH_VERTEX_INPUT
					#endif
				};

				struct PS_INPUT {
					float4 position : SV_POSITION;
					#if defined(HAS_OPACITY)
						GRAPH_PIXEL_INPUT
					#endif
				};

				// TODO: why does not this tinner buffer work?
				/*CBUFFER_START(c_per_object_depth_only)
					float4x4 world;
					float4x4 world_view_proj;
					float3 terrain_size;

					float4 dev_wireframe_color;
					#if defined(HAS_OPACITY)
						GRAPH_MATERIAL_EXPORTS
					#endif
				CBUFFER_END*/

				CBUFFER_START(c_per_object)
					float4x4 world;
					float4x4 world_view_proj;
					float3 terrain_size;
					float3 lod_camera_pos;
					float4x4 last_world;

					#if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER)
						float global_roughness_multiplier;
					#endif

					float4 dev_wireframe_color;
					GRAPH_MATERIAL_EXPORTS
				CBUFFER_END

				#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
					Buffer<float4> idata;
				#endif

				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				PS_INPUT vs_main(VS_INPUT input, uint instance_id : SV_InstanceId)
				{
					PS_INPUT o;
					float4 p;

					#if defined(HAS_OPACITY)
						GraphVertexParams params;
						GraphVertexResults results;
					#endif

					#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
						float4 pos_scale = idata.Load(instance_id * 2 + 0);
						float4 tile_info = idata.Load(instance_id * 2 + 1);			
					#endif
					float2 half_size = terrain_size.xy * 0.5;
					float2 mip = input.position.xy;				
					float2 pos = (mip.xy - 0.5) * pos_scale.zw + pos_scale.xy;

					float2 huv = ((pos.xy / half_size) * 0.5 + 0.5);
					#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
						huv.y = 1-huv.y;
					#endif
					float h = TEX2DLOD(hmap, huv, 0).r * terrain_size.z;	

					float4 position = float4(pos.xy, h, 1);

					#if defined(HAS_OPACITY)
						#if defined(NEEDS_WORLD_SPACE_POSITION)
							float4 wp = mul(position, world);
							// TODO: Expose output channel here
						#endif
					
						// Write automatic params
						GRAPH_VERTEX_WRITE_PARAMS(params, input);

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

						GRAPH_VERTEX_PARAM(params, terrain_uv) = huv;

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

						#if defined(NEEDS_TANGENT_SPACE)
							#if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM)
								float2 res;
								TEXTURE_NAME(hmap).GetDimensions(res.x, res.y);
								float2 inv_hmap_size = 1.0/res;
							#endif
							float3 normal = normal_from_hmap(hmap, float4(huv, 0, 0), inv_hmap_size, terrain_size.z);

							// create tangentspace vectors
							normal = mul(normal, (float3x3)world);
							float3 tangent = normalize(cross(world._m10_m11_m12, normal)); // TODO: redundant normalize?
							float3 binormal = normalize(cross(normal, tangent)); // TODO: redundant normalize?

							tspace_transpose(
								GRAPH_VERTEX_PARAM(params, tsm0), 
								GRAPH_VERTEX_PARAM(params, tsm1), 
								GRAPH_VERTEX_PARAM(params, tsm2), 
								tangent, binormal, normal);
						#endif

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

					p = mul(position, world_view_proj);

					#if defined(DEPTH_PREPASS)
						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
					
					#if defined(HAS_OPACITY)
						// Write results
						GRAPH_VERTEX_WRITE(o, results, params);
					#endif

					return o;
				}

				#if defined(DRAW_WIREFRAME)
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
					{
						return dev_wireframe_color;
					}
				#else
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					float4 ps_main(PS_INPUT input) : SV_TARGET0
					{
						#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

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