// in this context include refers to another shader file 
includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" ] 

render_states = {	
	filter = {
		inherits = "default"
		states = {
			z_write_enable = "false"
			z_enable = "false"
		}
	}

	brush = {
		inherits = "filter"
		states = {
			ndefined_SAMPLE_BASED = {
				ndefined_SAMPLE_HEIGHT = {
					ndefined_FLATTEN = {
						blend_enable = "true"
						defined_SUB = {
							blend_op = "blend_op_rev_sub"
						}
						ndefined_SUB = {				
							blend_op = "blend_op_add"
						}
						dest_blend = "blend_one"
						src_blend = "blend_one"				
					}
				}
			}
		}
	}	

	marker = {
		inherits = "opacity"
		states = {
			cull_mode = "cull_cw"
			z_func = "greater_equal"
		}
	}
	
	depth_only = {
		inherits = "default"
		states = {
			write_mask0 = "0x0"
			write_mask1 = "0x0"
			write_mask2 = "0x0"
			write_mask3 = "0x0"
		}
	}	
	
}

hlsl_shaders = {
	terrain_shared = {
		code="""
			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;				
			}
			
			#if defined(D3D11) || defined(D3D12) || defined(GNM)

			float3 normal_from_hmap(Texture2D<float> height_map, SamplerState s, float2 uv, float2 texel_size, float texel_aspect) {
				float4 h = {
					height_map.Sample(s, uv + texel_size * float2(-1, 0)).r,
					height_map.Sample(s, uv + texel_size * float2(1, 0)).r,
					height_map.Sample(s, uv + texel_size * float2(0, -1)).r,
					height_map.Sample(s, uv + texel_size * float2(0, 1)).r
				};
				
			#elif defined(GL2)

			float3 normal_from_hmap(sampler2D height_map, float2 uv, float2 texel_size, float texel_aspect) {						
				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);
			}
		"""
	}	
	terrain_editor_brush = {		
		includes = [ "common", "gbuffer_access" ]
		 
		samplers = {
			defined_SAMPLE_BASED = {
				input_texture0 = { sampler_states = "clamp_linear" }
			}			
		}

		code="""
			struct VS_INPUT {
				float4 position : POSITION;
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				#if defined(SAMPLE_HEIGHT)
					float world_height : TEXCOORD0;
					float layer_value : TEXCOORD1;
				#else
					float2 uv : TEXCOORD0;				
				#endif				
			};			 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float radius;
				float strength;
				float falloff;
				float mask_idx;
				float replay_layer_value;
				float3 replay_brush_pos;
				float3 mouse_pos;
				float4x4 inverse_terrain_wtm;
				float2 terrain_size;				
				float3 camera_ray_origin;
				float3 camera_ray_direction;
				#ifdef SAMPLE_BASED
					float2 inv_input_texture0_size;
				#endif
				float part_of_replay_stroke;
			CBUFFER_END
			
			Texture2D<float> linear_depth;
			#if defined(SAMPLE_HEIGHT)
				Texture2D<float4> output;
			#elif defined(SAMPLE_BASED)
				sampler2D input_texture0;
			#endif	
			
			#ifndef SAMPLE_HEIGHT
				Texture2D<float4> height_sample;
			#endif

			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;

				#if defined(SAMPLE_HEIGHT)
					// Reconstruct world height from pixel behind back buffer
					float3 smp = float3(mouse_pos.x/back_buffer_size.x, mouse_pos.z/back_buffer_size.y, 0) * 2 - 1;				
					float4 w = encode_world_pos(float4(smp,1));
					float d = gbuffer_decode_depth(linear_depth.Load(int3(mouse_pos.x, back_buffer_size.y-mouse_pos.z, 0)));
					//float d = gbuffer_decode_depth(depth.Load(int3(mouse_pos.x, mouse_pos.z, 0)));
					float3 wp = decode_world_pos(w, d);
					
					o.world_height = wp.z;				
					
					// Sample active layer value
					float3 terrain_pos = mul(float4(wp, 1), inverse_terrain_wtm);
					float2 half_size = terrain_size * 0.5;
					float2 huv = ((terrain_pos.xy / half_size) * 0.5 + 0.5);
					huv.y = 1-huv.y;
					 
					float2 layer_res;
					output.GetDimensions(layer_res.x, layer_res.y);				
					
					o.layer_value = output.Load(int3(layer_res * huv.xy,0))[(int)mask_idx];
				#else
					// Find camera ray/terrain intersection, terrain is represented as a plane with normal [0,0,1] and d=last sampled world height
					float3 wp;
					if(part_of_replay_stroke == 0)
					{
						float world_height = height_sample.Load(int3(0,0,0)).r;
						float t = (world_height - camera_ray_origin.z)/camera_ray_direction.z;
						wp = camera_ray_origin + camera_ray_direction * t;
					}
					else
					{
						wp = replay_brush_pos;
					}
					
					o.uv = input.position.xy;
					
					float3 terrain_pos = mul(float4(wp, 1), inverse_terrain_wtm);
					terrain_pos.xy = clamp(2*(terrain_pos.xy / terrain_size), -1, 1);

					float2 scale = (2*radius.xx) / terrain_size;
					#if defined(SAMPLE)
						// Increase brush size with filter kernel size
						scale += inv_input_texture0_size*2;
					#endif
					input.position.xy *= scale;
					input.position.xy += terrain_pos.xy;
				#endif
				
				o.position =  mul(input.position, world_view_proj);
				return o;
			}			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) :  SV_TARGET0 {		
				const float4 mask_lookup[4] = {
					float4(1,0,0,0),
					float4(0,1,0,0),
					float4(0,0,1,0),
					float4(0,0,0,1)					
				};
				
				#ifdef SAMPLE_HEIGHT
					return float4(input.world_height, input.layer_value, 0, 0);
				#else
					float phase =  saturate(length(input.uv)) * (3.1415926*0.5);
					float a = cos(phase);
					
					#if defined(FLATTEN)						
						float2 layer_uv = input.position.xy * inv_input_texture0_size;
						float4 center = tex2D(input_texture0, layer_uv);						
						float4 v = center;

						float height;
						if(part_of_replay_stroke == 0)
						{
							height = height_sample.Load(int3(0,0,0)).g;
						}
						else
						{
							height = replay_layer_value;
						}

						v = (1-mask_lookup[(int)mask_idx]) * v + mask_lookup[(int)mask_idx] * lerp(center[(int)mask_idx], height, a * strength);
						return v;
					#elif defined(SAMPLE)
						float2 layer_uv = input.position.xy * inv_input_texture0_size;
						float4 c = tex2D(input_texture0, layer_uv);
						return c;						
					#else
						a = pow(a, falloff);
					
						#if defined(BOX_FILTER)
							float d = 0.5;
							
							float2 layer_uv = input.position.xy * inv_input_texture0_size;
							float4 center = tex2D(input_texture0, layer_uv);
							float4 c =
								tex2D(input_texture0, layer_uv + half2(-d, -d) * inv_input_texture0_size) +
								tex2D(input_texture0, layer_uv + half2( d, -d) * inv_input_texture0_size) + 
								tex2D(input_texture0, layer_uv + half2(-d,  d) * inv_input_texture0_size) + 
								tex2D(input_texture0, layer_uv + half2( d,  d) * inv_input_texture0_size);
								
							c *= 0.25;
							float4 v = center;
							float4 mask = mask_lookup[(int)mask_idx];							
							v = (1-mask) * v + mask * lerp(center[(int)mask_idx], c[(int)mask_idx], saturate(a * strength * 10.0));
							return v;
						#else							
							#if defined(INVERT)	
								const float4 invert_mask_lookup[4] = {
									float4(0,1,1,1),
									float4(0,0,1,1),
									float4(0,0,0,1),
									float4(0,0,0,0)					
								};					
								float4 v = invert_mask_lookup[(int)mask_idx] * strength * a;
							#else
								float4 v = mask_lookup[(int)mask_idx] * strength * a;
							#endif
							return v;					
						#endif
					#endif
				#endif				
			}	
		"""
	}	
	
	terrain_editor_brush_marker = {		
		includes = [ "common", "gbuffer_access" ]
		 
		code="""
			struct VS_INPUT {
				float4 position : POSITION;
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				float4 w : TEXCOORD0;
				float3 center : TEXCOORD1;
			};		
			
			Texture2D<float> linear_depth;
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float4x4 world;
				float radius;
				float falloff;
				float strength;
				float3 brush_color;
				float brush_type;
				float3 mouse_pos;
				float3 camera_ray_origin;  
				float3 camera_ray_direction;				
			CBUFFER_END
			
			Texture2D<float> height_sample;
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o; 
				
				// Find camera ray/terrain intersection, terrain is represented as a plane with normal [0,0,1] and d=last sampled world height
				float world_height = height_sample.Load(int3(0,0,0)).r;
				float t = (world_height - camera_ray_origin.z)/camera_ray_direction.z;					
				float3 wp = camera_ray_origin + camera_ray_direction * t;
				
				input.position.xy *= radius;
				input.position.z *= 1000;				
				input.position.xyz += wp;				
				o.position =  mul(input.position, world_view_proj);				
				o.center = wp; 
				o.w = encode_world_pos(o.position);
				return o;
			}			
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			float4 ps_main(PS_INPUT input) :  SV_TARGET0 {
				float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy,0)));
				float3 wp = decode_world_pos(input.w, d);
				wp.z = 0;
				float l = length(float3(wp.xy,0)-float3(input.center.xy, 0));
				
				float stroke_width = 0.02 * radius;
				float falloff_marker = (1-(0.5 * falloff)) * radius;				
				float4 c;
				
				if (brush_type == 0) {
					float3 mark = l < stroke_width ? float3(1,1,1) : ((l > falloff_marker-stroke_width && l < falloff_marker) || (l > radius-stroke_width && l < radius)) ? brush_color : float3(0,0,0);
					c = float4(mark, dot(mark, float3(1,1,1)) == 0 ? 0 : lerp(0.2, 0.6, strength));
				} else {
					float fade = (l > (falloff_marker-stroke_width) && l < radius) ? (1-saturate((l - falloff_marker) / (radius - falloff_marker))) : 1;				
					float3 mark = l < stroke_width ? float3(1,1,1) : (l < radius) ? brush_color : float3(0,0,0);
					c = float4(mark, dot(mark, float3(1,1,1)) == 0 ? 0 : fade * lerp(0.2, 0.6, strength));
				}		
				return c;
				
			}	
		"""
	}
	
	terrain_decoration = {		
		includes = [ "common", "gbuffer_access", "terrain_shared" ]
		samplers = {
			defined_D3D11 = {
				clamp_linear = { sampler_states = "clamp_linear" }
				input_texture1 = { sampler_states = "clamp_linear" }
			}
			defined_D3D12 = {
				clamp_linear = { sampler_states = "clamp_linear" }
				input_texture1 = { sampler_states = "clamp_linear" }
			}
			ndefined_D3D11 = {
				ndefined_D3D12 = {
					input_texture0 = { sampler_states = "clamp_linear" }
					input_texture1 = { sampler_states = "clamp_linear" }
				}
			}
		}
		 
		code="""
			#if defined(D3D11) || defined(D3D12)
				SamplerState clamp_linear;
				Texture2D<float> input_texture0;
				sampler2D input_texture1;
			#elif defined(GL2)
				sampler2D input_texture0;
				sampler2D input_texture1;
			#endif
						
			struct VS_INPUT {
				float4 position : POSITION;
				float2 uv : TEXCOORD0;				
			};
			
			struct PS_INPUT {
				float4 position : SV_POSITION;
				float2 uv : TEXCOORD0;
			};			 			
			
			CBUFFER_START(c0)
				float4x4 world_view_proj;
				float2 scale;
				float2 offset;
				float height;
				#if defined(GL2)
					float2 inv_input_texture0_size;
				#endif
			CBUFFER_END
			
			struct DECORATION_OUT { 
				half4 buffer0 : COLOR0;
				half4 buffer1 : COLOR1;
			};
			
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			PS_INPUT vs_main(VS_INPUT input) {
				PS_INPUT o;
				o.position = mul(input.position, world_view_proj);				
				float2 uv = input.uv;		
				o.uv = uv * scale + offset;
				#if defined(D3D11) || defined(D3D12)
					o.uv.y = 1-o.uv.y;
				#endif
				
				return o;
			}			
						
			DEFAULT_ROOT_SIGNATURE_ATTRIBUTE
			DECORATION_OUT ps_main(PS_INPUT input) {
				DECORATION_OUT o;
				#if defined(D3D11) || defined(D3D12)
					float2 res;
					input_texture0.GetDimensions(res.x, res.y);
					float3 hmap_normal = normal_from_hmap(input_texture0, clamp_linear, input.uv, 1.0/res, height);
				#elif defined(GL2)
					float3 hmap_normal = normal_from_hmap(input_texture0, input.uv, inv_input_texture0_size, height);
				#endif
				
				float3 n = encode_signed_normal(hmap_normal);
				
				#if defined(D3D11) || defined(D3D12)
					float h = input_texture0.Sample(clamp_linear, input.uv).r;
				#elif defined(GL2)
					float h = tex2D(input_texture0, input.uv).r;
				#endif
				o.buffer0 = float4(encode_float_rg(h), n.xy /*encode_float_rg(h)*/);
				float4 mat = tex2D(input_texture1, input.uv);
				o.buffer1 = mat;
				return o;
			}	
		"""
	}		
}

shaders = {
	terrain_editor_brush = {
		editor_advanced_mode = true
		
		contexts = {			
			default = {
				passes_sort_mode="immediate"
				passes = [
					{ hlsl_shader="terrain_editor_brush" render_states="brush" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}
	
	terrain_editor_brush_marker = {
		editor_advanced_mode = true
		
		contexts = {
			default = {
				passes = [
					{ layer="transparent" hlsl_shader="terrain_editor_brush_marker" render_states="marker" }
				]
			}
		}	
		
		compile = {
			default = [
				{ defines=[] }
			]
		} 
	}	
	
	terrain_decoration = {
		editor_advanced_mode = true		
		
		contexts = {
			default = {
				passes_sort_mode="immediate"
				passes = [{
					defined="OES2"
					pass = [
					]
					fail = [
						{ hlsl_shader="terrain_decoration" render_states="filter" }
					]
				}]
			}
		}	
		
		compile = {			
			default = [
				{ defines=[] }
			]
		} 
	}
}

static_compile = [ 
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" } 
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SUB"] } 
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SUB" "INVERT"] } 
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_HEIGHT"] } 	
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_BASED" "FLATTEN"] } 	
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_BASED" "SAMPLE"] }
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_BASED" "BOX_FILTER"] } 	
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush_marker" }
	{ if: "on_renderer(D3D11, D3D12)" shader="terrain_decoration" }
]