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

// TODO: disable sun if it's disabled in environment settings.
// TODO: options to disable shadows, local lights, sun_light.

inputs = {
	"e1d24468-d03e-4884-9b0d-dba23aaa94d6" = {
		name = "base_color"
		is_required = false
		display_name = "Base Color"
		type = { vector3: ["HAS_BASE_COLOR"] }
		domain = "pixel"
	}
	
	"0752e133-0c01-4fc4-b45a-a629cad4f850" = {
		name = "opacity"
		is_required = false
		display_name = "Opacity"
		type = { scalar: ["HAS_OPACITY"] }
		domain = "pixel"
	}
/*
	"6fd5ebe2-4d15-4ecc-abad-ab9731ea861a" = {
		name = "fade_range"
		is_required = false
		display_name = "Angle Fade Range"
		type = { vector2: ["HAS_ANGLE_FADE" "NEEDS_EYE_VECTOR"] }
		domain = "pixel"
	}
*/
}

options = {
	"e6347231-6647-4bc3-a1d0-f24e1b3e0562" = "EXTERNAL_ROTATION"
	"914232a9-5352-43d7-a877-cedfe798541d" = "TANGENT_LOCKED"
	"e8e86308-83cd-47a1-ab84-82776b3cf0ca" = "SCREEN_SPACE"
	"9775d054-ef17-4ca4-b5a4-8751c6e1eec6" = "PIVOT"
	"aaebf627-efab-4c86-8244-359d29d00f33" = "ROTATION"
	"47a74b09-1932-40d4-ab90-0ec090fb9643" = "BLEND_ADDITIVE"
	"e267562a-65dc-4547-a9fa-6a605b451cae" = "BLEND_PREMULTIPLIED_ALPHA"
	"4a42f2e1-1069-4afe-a93f-c7558572780f" = "EMISSIVE_PARTICLES"
	"5e380f16-5cc6-482b-9df6-af0d2b3bda3c" = "HAS_CUSTOM_FOV"
	"fea9af88-d417-433a-b475-346b6b03e249" = "HIGH_QUALITY"
}

ui = [

	{
		type = "drop_down"
		display_name = "Blend Mode"
		options = {
			"Alpha Blend"			 		= "00000000-0000-0000-0000-000000000000"
			"Additive Blend"			 	= "47a74b09-1932-40d4-ab90-0ec090fb9643"
		}
		default = "00000000-0000-0000-0000-000000000000"
	}

	{ type = "checkbox" display_name = "Emissive particles" option = "4a42f2e1-1069-4afe-a93f-c7558572780f" }
	{ type = "checkbox" display_name = "Custom FOV" 		option = "5e380f16-5cc6-482b-9df6-af0d2b3bda3c" }
]

render_state = {

	ribbon_opacity = {
		inherit: ["core/stingray_renderer/shader_libraries/common#opacity"]
		state: {
			blend_op = "blend_op_add"
			src_blend = "blend_one"
			dest_blend = "blend_inv_src_alpha"
			write_mask0 = "red|green|blue"
			z_enable = "true"
		}
	}
}

sampler_state = {
}

channels = 	{

	"defined(PARTICLE_LIGHTING)": {
		basis0 = { type = "float4" domains = ["vertex", "pixel"] }
		basis1 = { type = "float4" domains = ["vertex", "pixel"] }
		basis2 = { type = "float4" domains = ["vertex", "pixel"] }
		back_lighting = { type = "float3" domains = ["vertex", "pixel"] }
	}
/*
	"defined(HAS_ANGLE_FADE)": {
		world_space_plane_normal = { type = "float3" domains = ["vertex", "pixel"] }
	}

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

	"defined(NEEDS_WORLD_SPACE_NORMAL)": {
		world_space_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" }
	}
*/	
	strip_info = { type = "float" semantic = "POSITION1" domain = "vertex" }
	vertex_size = { type = "float2" semantic = "TEXCOORD7" domain = "vertex" }
	
	
	"defined(NEEDS_UV_SCALE)": {
		"defined(NEEDS_UV_ANIMATION)": {
			vertex_uv_data = { type = "float3" semantic = "TEXCOORD0" domains = ["vertex"] }
			uv_frame = { type = "float" domains = ["vertex"] }
		}
		"!defined(NEEDS_UV_ANIMATION)": {
			vertex_uv_data = { type = "float2" semantic = "TEXCOORD0" domains = ["vertex"] }
		}
		uv_scale = { type = "float2" domains = ["vertex"] }
	}
	"!defined(NEEDS_UV_SCALE)": {
		"defined(NEEDS_UV_ANIMATION)": {
			vertex_uv_frame = { type = "float" semantic = "TEXCOORD0" domains = ["vertex"] }
			uv_frame = { type = "float" domains = ["vertex"] }
		}
	}
	vertex_tangent = { type = "float3" semantic = "TANGENT" domains = ["vertex"] }
	ribbon_distance = { type = "float" semantic = "TEXCOORD6" domains = ["vertex"] }
	
}

log_permutations = false
permutation_sets = {
    vertex_modifiers = [
    ]

    default = [
    ]
}

shader_contexts = {
	default = {
		passes_sort_mode = "deferred"
		compile_with = [
			{ if: "on_renderer(D3D11, D3D12, GNM, GL)" defines=["LOCAL_LIGHTS"] }
		]

		passes = [
			{ if: "defined(EMISSIVE_PARTICLES)" then: [
					{ layer="hdr_transparent" code_block="ribbon" render_state="ribbon_opacity" }
				] else: [
					{ layer="hdr_transparent" code_block="ribbon" render_state="ribbon_opacity"  defines=["PARTICLE_LIGHTING" "VS_FOG"] }
			]}
		]
	}
}

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

				#if defined(SHADOW_RECEIVING)
					#define CALCULATE_LIGHTING
				#endif
			#endif
		"""
	}

	ribbon = {
		language = "hlsl"

		include:[
			"particle_lighting"
			"core/stingray_renderer/shader_libraries/common#common",
			"core/stingray_renderer/shader_libraries/common#gbuffer_access",
			"core/stingray_renderer/shader_libraries/common#fog",
			"core/stingray_renderer/shader_libraries/lighting_common#brdf",
			"core/stingray_renderer/shader_libraries/common#taa_offsets",
			"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#clustered_shading"
			"core/stingray_renderer/shader_libraries/particle_lighting_common#radiosity_normal_mapping"
			"core/stingray_renderer/shader_libraries/particle_lighting_common#particle_debug" ]

		instance_data = {
		}

		stage_conditions = {
		}

		samplers = {
			"defined(DISTORTION)": {
				hdr0 = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
					source = "resource_set"
					slot_name = "hdr0"
					type = "2d"
				}
			}
			"defined(PARTICLE_LIGHTING)": {
				global_diffuse_map = {
					sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear"
					source = "resource_set"
					slot_name = "global_diffuse_map"
					type = "cube"
				}
			}
			"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"
				}
			}
			
		}

		code = """
			#if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) 
				#define NEEDS_TANGENT_SPACE
			#endif

			#if defined(NEEDS_PIXEL_DEPTH) || defined(PARTICLE_LIGHTING)
				#define PS_NEEDS_WP
			#endif

			#if defined(NEEDS_LINEAR_DEPTH)
				DECLARE_SAMPLER_2D(linear_depth);
				#define HAS_LINEAR_DEPTH
			#endif

			struct VS_INPUT {
				float4 position : POSITION;	
				GRAPH_VERTEX_INPUT
			};

			struct PS_INPUT {
				float4 position : SV_POSITION;	
				#if defined(PS_NEEDS_WP)
					float3 world_pos : TEXCOORD15;
				#endif
				#if defined(VS_FOG)
					float4 fog_params : TEXCOORD16;
				#endif
				GRAPH_PIXEL_INPUT
			};

			CBUFFER_START(c_ribbon)
					float4x4 view;
					float4x4 view_proj;
				#if defined(EMISSIVE_PARTICLES)
					float emissive_particle_intensity;
				#endif
			CBUFFER_END
			
			CBUFFER_START(c_material_exports)
				GRAPH_MATERIAL_EXPORTS
			CBUFFER_END
			

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input)
			{
				PS_INPUT o;

				GraphVertexParams params;
				GraphVertexResults results;

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

				
				float3 y = GRAPH_VERTEX_DATA(input, vertex_tangent);
				float3 x = normalize(cross(normalize(input.position.xyz - camera_pos), y));
				
				float3 x_axis = x;
				float3 y_axis = y;

				float strip_info = GRAPH_VERTEX_DATA(input, strip_info);
				float2 size = GRAPH_VERTEX_DATA(input, vertex_size);
				float corner = strip_info * (size.x * 0.5);
				
				float3 wp = input.position.xyz + (x_axis * corner);

				#if defined(HAS_CUSTOM_FOV)
					float4 p = mul(float4(wp, 1),camera_custom_fov_view_projection);
				#else
					float4 p = mul(float4(wp, 1), view_proj);
				#endif


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

				// TOOD: Move this code to its own shader node!
				#if defined(NEEDS_UV_SCALE)
					#if defined(NEEDS_UV_ANIMATION)
						GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_data).z;
					#endif 
					GRAPH_VERTEX_PARAM(params, uv_scale) = GRAPH_VERTEX_DATA(input, vertex_uv_data).xy;
				#elif defined(NEEDS_UV_ANIMATION)
					GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_frame);
				#endif

				
				#if defined(PARTICLE_LIGHTING)
					float3 normal = normalize(lerp(wp - input.position.xyz, -view._m01_m11_m21, 0.2));						
					normal = mul(normal, (float3x3)view);
				#endif
				
				
				#if defined(PARTICLE_LIGHTING)
					float4 basis0 = float4(0,0,0,1);
					float4 basis1 = float4(0,0,0,1);
					float4 basis2 = float4(0,0,0,1);
					float3 back_lighting = float3(0,0,0);
					calc_basis_lighting(basis0, basis1, basis2, back_lighting, wp, normal, view, p);
					GRAPH_VERTEX_PARAM(params, basis0) = basis0;
					GRAPH_VERTEX_PARAM(params, basis1) = basis1;
					GRAPH_VERTEX_PARAM(params, basis2) = basis2;
					#if defined(BACK_LIGHTING)
						GRAPH_VERTEX_PARAM(params, back_lighting) = back_lighting;
					#endif
				#endif
				
				
				// Evaluate all pieces of the graph that should run per-vertex.
				GRAPH_EVALUATE_VERTEX(results, params);
				
				
				#if defined(PS_NEEDS_WP)
					o.world_pos = wp;
				#endif
				
				#if defined(VS_FOG)
						const float3 view_dir = camera_world._m30_m31_m32 - wp.xyz;
						const float3 camera_dir = camera_world._m10_m11_m12;
						const float depth = dot(-view_dir, camera_dir);
						o.fog_params = calc_fog_vs(wp, depth);
				#endif
				
				o.position = p;

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

				return o;
			}
			
			/*
			#elif defined(DISTORTION) // TODO: avoid sample from objects infront of the "distortion"
				DECLARE_SAMPLER_2D(hdr0);
				#if !defined(LOW_RES) && defined(LOW_RES_ENABLED)
					DECLARE_SAMPLER_2D(hdr_transparent_div4);
					DECLARE_SAMPLER_2D(hdr_linear_depth_div4);
				#endif

				#ifdef LOW_RES
					struct PS_OUTPUT {
						half4 color : SV_TARGET0;
						half4 depth : SV_TARGET1;
					};
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					PS_OUTPUT ps_main(PS_INPUT input)
				#else
					DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
					half4 ps_main(PS_INPUT input) : SV_TARGET0
				#endif
				{
					GraphPixelParams params;
					GraphPixelResults graph;

					GRAPH_PIXEL_WRITE_PARAMS(params, input);

					#if (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) || defined(NEEDS_SCREEN_POS)
						#if defined(LOW_RES)
							float2 screen_position = input.position.xy / (back_buffer_size * 0.25);
						#else
							float2 screen_position = (input.position.xy / back_buffer_size);
						#endif
					#endif

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

					#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

					GRAPH_EVALUATE_PIXEL(graph, params);

					float3 normal = graph.distortion_normal;
					normal.rg = normal.rg * 2 - 1;
					half2 distortion = normal.xy / back_buffer_size;

					#if defined(HAS_OPACITY)
						half opacity = saturate(graph.opacity);
					#else
						half opacity = 0.5;
					#endif

					#if !defined(LOW_RES) && defined(LOW_RES_ENABLED)
						// 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_position).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_position).a * (1.0 - T);
						opacity *= 1.0 - alpha_modifier;
					#endif
							
					float3 color = TEX2D(hdr0, screen_position + (distortion * graph.distortion_strength)).rgb;

					#if defined(HAS_BASE_COLOR)
						color *= graph.base_color;
					#endif
					
					#ifdef LOW_RES		
						PS_OUTPUT o;
						o.color = half4(color * opacity, opacity);
						float alpha_depth = depth * opacity;
						o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0.0, opacity);
						return o;
					#else
						#if defined(PARTICLE_DEBUG)
							return DISTORTION_PARTICLES_DEBUG_COLOR;
						#else
							return half4(color * opacity, opacity);
						#endif		
					#endif
				}
			#else
			*/
				DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
				half4 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
					
					GRAPH_EVALUATE_PIXEL(graph, params);

					#if defined(HAS_OPACITY)
						half opacity = saturate(graph.opacity);
					#else
						half opacity = 0.5;
					#endif
					
					
					
					#if defined(HAS_BASE_COLOR)
						float4 color = float4(graph.base_color, opacity);
					#else
						float4 color = float4(1,1,1, opacity);
					#endif 
					
					
					#if defined(PARTICLE_LIGHTING)
							float4 basis0 = GRAPH_PIXEL_DATA(input, basis0);
							float4 basis1 = GRAPH_PIXEL_DATA(input, basis1);
							float4 basis2 = GRAPH_PIXEL_DATA(input, basis2);
							#if defined(BACK_LIGHTING)
								float3 back_lighting = GRAPH_PIXEL_DATA(input, back_lighting);
							#endif
							float3 normal = normalize(float3(basis0.w, basis1.w, basis2.w));
						color.rgb = calc_lighting(color, normal, back_lighting, basis0.xyz, basis1.xyz, basis2.xyz);
						#if defined(VS_FOG)
							// apply vs calculated fog
							color.rgb = lerp(color.rgb, input.fog_params.rgb, input.fog_params.a);
						#else
							color = apply_fog(color, world_pos, depth);
						#endif
					#elif defined(EMISSIVE_PARTICLES)
						color.rgb *= emissive_particle_intensity;
					#endif
					
					
					/*
					#if defined(HAS_ANGLE_FADE)
						float3 plane_wn = normalize(GRAPH_PIXEL_DATA(input, world_space_plane_normal));
						float3 eye_dir = normalize(GRAPH_PIXEL_DATA(input, eye_vector));
						float fade = dot(plane_wn, eye_dir);
						opacity *= saturate((fade - graph.fade_range.x) / (graph.fade_range.y - graph.fade_range.x));
					#endif
					*/
					
					half4 output_color = half4(color.rgb * opacity, opacity);
					#if defined(BLEND_ADDITIVE)
						output_color.a = 0.0;
					#endif

					return output_color;
				}			
			//#endif
		"""
	}
}