@property( false )
@insertpiece( SetCrossPlatformSettings )
@insertpiece( SetCompatibilityLayer )

layout(std140) uniform;
#define FRAG_COLOR		0
layout(location = FRAG_COLOR) out vec4 outColour;

uniform sampler3D terrainData;

in block
{
@insertpiece( Ocean_VStoPS_block )
} inPs;

void main()
{
	outColour = vec4( inPs.uv0.xy, 0.0, 1.0 );
}

@end
@property( !false )
@insertpiece( SetCrossPlatformSettings )
@property( GL3+ < 430 )
	@property( hlms_tex_gather )#extension GL_ARB_texture_gather: require@end
@end

@property( hlms_amd_trinary_minmax )#extension GL_AMD_shader_trinary_minmax: require@end
@insertpiece( SetCompatibilityLayer )

layout(std140) uniform;
#define FRAG_COLOR		0
layout(location = FRAG_COLOR) out vec4 outColour;

@property( hlms_vpos )
in vec4 gl_FragCoord;
@end

// START UNIFORM DECLARATION
@insertpiece( PassDecl )
@insertpiece( OceanMaterialDecl )
@insertpiece( OceanInstanceDecl )
@insertpiece( custom_ps_uniformDeclaration )
// END UNIFORM DECLARATION

in block
{
@insertpiece( Ocean_VStoPS_block )
} inPs;

uniform sampler3D terrainData;
uniform sampler2D blendMap;

@property( hlms_forwardplus )
/*layout(binding = 1) */uniform usamplerBuffer f3dGrid;
/*layout(binding = 2) */uniform samplerBuffer f3dLightList;
@end
@property( num_textures )uniform sampler2DArray textureMaps[@value( num_textures )];@end
@property( envprobe_map )uniform samplerCube	texEnvProbeMap;@end

vec4 diffuseCol;
@insertpiece( FresnelType ) F0;
float ROUGHNESS;

vec3 nNormal;

@property( hlms_lights_spot_textured )@insertpiece( DeclQuat_zAxis )
vec3 qmul( vec4 q, vec3 v )
{
	return v + 2.0 * cross( cross( v, q.xyz ) + q.w * v, q.xyz );
}
@end

@property( detail_maps_normal )vec3 getTSDetailNormal( sampler2DArray normalMap, vec3 uv )
{
	vec3 tsNormal;
@property( signed_int_textures )
	// Normal texture must be in U8V8 or BC5 format!
	tsNormal.xy = texture( normalMap, uv ).xy;
@end @property( !signed_int_textures )
	// Normal texture must be in LA format!
	tsNormal.xy = texture( normalMap, uv ).xw * 2.0 - 1.0;
@end
	tsNormal.z	= sqrt( max( 0.0, 1.0 - tsNormal.x * tsNormal.x - tsNormal.y * tsNormal.y ) );

	return tsNormal;
}
	@foreach( 4, n )
		@property( normal_weight_detail@n )
			@piece( detail@n_nm_weight_mul ) * material.normalWeights.@insertpiece( detail_swizzle@n )@end
		@end
	@end
@end

@insertpiece( DeclareBRDF )

@insertpiece( DeclShadowMapMacros )
@insertpiece( DeclShadowSamplers )
@insertpiece( DeclShadowSamplingFuncs )

void main()
{
	@insertpiece( custom_ps_preExecution )

	@insertpiece( custom_ps_posMaterialLoad )

	// ===== CONFIGURABLE PARAMETERS FROM DATABLOCK =====
	float baseRoughness    = material.roughness.x;
	float foamRoughness    = material.roughness.y;
	float transparency     = material.roughness.z;
	float ambientReduction = material.metalness.x;
	float diffuseScale     = material.metalness.y;
	float foamIntensity    = material.metalness.z;
	float reflectionStrength = material.deepColour.w;

	// ===== Sample ocean texture data =====
	vec4 textureValue  = textureLod( terrainData, vec3(inPs.uv1.xy, inPs.uv1.z), 0.0 );
	vec4 textureValue2 = textureLod( terrainData, vec3(inPs.uv2.xy, inPs.uv2.z), 0.0 );
	vec4 textureValue3 = textureLod( terrainData, vec3(inPs.uv3.xy, inPs.uv3.z), 0.0 );
	vec4 textureValue4 = textureLod( terrainData, vec3(inPs.uv4.xy, inPs.uv4.z), 0.0 );

	textureValue = mix(textureValue, textureValue2, inPs.blendWeight.x);
	textureValue = mix(textureValue, textureValue3, inPs.blendWeight.y);
	textureValue = mix(textureValue, textureValue4, inPs.blendWeight.z);
	
	// ===== Wave calculations =====
	float waveIntensity = clamp( inPs.wavesIntensity, 0.0, 1.0 );
	float waveFactor    = clamp( inPs.waveHeight, 0.0, 1.0 ) * waveIntensity;

	// ===== Foam calculation (configurable intensity) =====
	float foam = pow( clamp( textureValue.w, 0.0, 1.0 ), 2.0 ) * waveFactor * 0.5 * foamIntensity;
	foam = clamp( foam, 0.0, 1.0 );

	// ===== Roughness (configurable base and foam) =====
	ROUGHNESS = mix( baseRoughness, foamRoughness, foam );

	// ===== Base water color =====
	diffuseCol.xyz = mix( material.deepColour.xyz, material.shallowColour.xyz, waveFactor * 0.7 );
	vec3 foamColour = vec3( 0.82, 0.86, 0.9 );
	diffuseCol.xyz = mix( diffuseCol.xyz, foamColour, foam );
	diffuseCol.w = 1.0;

	// ===== Normal calculation =====
	// Normal from texture (baked waves)
	vec3 nNormal;
	nNormal.xy = textureValue.xy * 2.0 - 1.0;
	nNormal.xy *= waveIntensity;
	nNormal.z  = 1.0;
	nNormal    = normalize( nNormal );

	// Flat ocean surface normal in VIEW space
	vec3 geomNormal = vec3( 0.0, 1.0, 0.0 );
	geomNormal = normalize( mat3( passBuf.view ) * geomNormal );

	// View-space unit X axis (from view matrix)
	vec3 viewSpaceUnitX = vec3(
		passBuf.view[0].x,
		passBuf.view[1].x,
		passBuf.view[2].x
	);

	// Build stable TBN (NO derivatives!)
	vec3 tangent   = normalize( cross( geomNormal, viewSpaceUnitX ) );
	vec3 bitangent = cross( tangent, geomNormal );

	// Transform normal from texture into view space
	nNormal = normalize(
		bitangent * nNormal.x +
		tangent   * nNormal.y +
		geomNormal * nNormal.z
	);

	@insertpiece( custom_ps_posSampleNormal )

	// ===== Diffuse (configurable scale) =====
	diffuseCol.xyz *= material.kD.xyz * diffuseScale;

	// ===== Specular (boost for reflections, reduced by foam) =====
	float specAtten = mix( 1.5, 0.6, foam );
	vec3 specular = vec3( specAtten, specAtten, specAtten );

	// Fresnel - water (non-metallic)
	float metalness = 0.0;
	F0 = mix( vec3( 0.08 ), material.kD.xyz * 3.14159, metalness );

	// ===== Shadows =====
	@property( !(hlms_pssm_splits || (!hlms_pssm_splits && hlms_num_shadow_map_lights && hlms_lights_directional)) )
		float fShadow = 1.0;
	@end
	@insertpiece( DoDirectionalShadowMaps )

	// Everything's in Camera space
@property( hlms_lights_spot || ambient_hemisphere || envprobe_map || hlms_forwardplus )
	vec3 viewDir	= normalize( -inPs.pos );
	float NdotV		= clamp( dot( nNormal, viewDir ), 0.0, 1.0 );@end

@property( !ambient_fixed )
	vec3 finalColour = vec3(0);
@end @property( ambient_fixed )
	vec3 finalColour = passBuf.ambientUpperHemi.xyz * diffuseCol.xyz;
@end

	@insertpiece( custom_ps_preLights )

@property( !custom_disable_directional_lights )
@property( hlms_lights_directional )
	finalColour += BRDF( passBuf.lights[0].position.xyz, viewDir, NdotV, passBuf.lights[0].diffuse, passBuf.lights[0].specular ) @insertpiece(DarkenWithShadowFirstLight);
@end
@foreach( hlms_lights_directional, n, 1 )
	finalColour += BRDF( passBuf.lights[@n].position.xyz, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular )@insertpiece( DarkenWithShadow );@end
@foreach( hlms_lights_directional_non_caster, n, hlms_lights_directional )
	finalColour += BRDF( passBuf.lights[@n].position.xyz, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular );@end
@end

@property( hlms_lights_point || hlms_lights_spot )	vec3 lightDir;
	float fDistance;
	vec3 tmpColour;
	float spotCosAngle;@end

	// Point lights
@foreach( hlms_lights_point, n, hlms_lights_directional_non_caster )
	lightDir = passBuf.lights[@n].position.xyz - inPs.pos;
	fDistance= length( lightDir );
	if( fDistance <= passBuf.lights[@n].attenuation.x )
	{
		lightDir *= 1.0 / fDistance;
		tmpColour = BRDF( lightDir, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular )@insertpiece( DarkenWithShadowPoint );
		float atten = 1.0 / (0.5 + (passBuf.lights[@n].attenuation.y + passBuf.lights[@n].attenuation.z * fDistance) * fDistance );
		finalColour += tmpColour * atten;
	}@end

	// Spot lights
@foreach( hlms_lights_spot, n, hlms_lights_point )
	lightDir = passBuf.lights[@n].position.xyz - inPs.pos;
	fDistance= length( lightDir );
@property( !hlms_lights_spot_textured )	spotCosAngle = dot( normalize( inPs.pos - passBuf.lights[@n].position.xyz ), passBuf.lights[@n].spotDirection );@end
@property( hlms_lights_spot_textured )	spotCosAngle = dot( normalize( inPs.pos - passBuf.lights[@n].position.xyz ), zAxis( passBuf.lights[@n].spotQuaternion ) );@end
	if( fDistance <= passBuf.lights[@n].attenuation.x && spotCosAngle >= passBuf.lights[@n].spotParams.y )
	{
		lightDir *= 1.0 / fDistance;
	@property( hlms_lights_spot_textured )
		vec3 posInLightSpace = qmul( spotQuaternion[@value(spot_params)], inPs.pos );
		float spotAtten = texture( texSpotLight, normalize( posInLightSpace ).xy ).x;
	@end
	@property( !hlms_lights_spot_textured )
		float spotAtten = clamp( (spotCosAngle - passBuf.lights[@n].spotParams.y) * passBuf.lights[@n].spotParams.x, 0.0, 1.0 );
		spotAtten = pow( spotAtten, passBuf.lights[@n].spotParams.z );
	@end
		tmpColour = BRDF( lightDir, viewDir, NdotV, passBuf.lights[@n].diffuse, passBuf.lights[@n].specular )@insertpiece( DarkenWithShadow );
		float atten = 1.0 / (0.5 + (passBuf.lights[@n].attenuation.y + passBuf.lights[@n].attenuation.z * fDistance) * fDistance );
		finalColour += tmpColour * (atten * spotAtten);
	}@end

@insertpiece( forward3dLighting )

@property( envprobe_map || ambient_hemisphere )
	vec3 reflDir = 2.0 * dot( viewDir, nNormal ) * nNormal - viewDir;

	@property( envprobe_map )
		vec3 envColourS = textureLod( texEnvProbeMap, reflDir * passBuf.invViewMatCubemap, ROUGHNESS * 12.0 ).xyz @insertpiece( ApplyEnvMapScale );
		vec3 envColourD = textureLod( texEnvProbeMap, nNormal * passBuf.invViewMatCubemap, 11.0 ).xyz @insertpiece( ApplyEnvMapScale );
		@property( !hw_gamma_read )	// Gamma to linear space
			envColourS = envColourS * envColourS;
			envColourD = envColourD * envColourD;
		@end
	@end
	@property( ambient_hemisphere )
		float ambientWD = dot( passBuf.ambientHemisphereDir.xyz, nNormal ) * 0.5 + 0.5;
		float ambientWS = dot( passBuf.ambientHemisphereDir.xyz, reflDir ) * 0.5 + 0.5;

		@property( envprobe_map )
			// Configurable ambient reduction
			envColourS += mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWD ) * (1.0 - ambientReduction * 0.5);
			envColourD += mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWS ) * (1.0 - ambientReduction * 0.5);
		@end @property( !envprobe_map )
			vec3 envColourS = mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWD );
			vec3 envColourD = mix( passBuf.ambientLowerHemi.xyz, passBuf.ambientUpperHemi.xyz, ambientWS );
		@end
	@end

	@insertpiece( BRDF_EnvMap )
	
	// Apply configurable reflection strength
	// Note: In GLSL, the BRDF_EnvMap piece already calculated Rs
	// We need to scale it by reflectionStrength
	// This assumes BRDF_EnvMap defines a variable like 'envSpecular' or similar
	// If not, you may need to adjust based on your actual BRDF_EnvMap implementation
@end

@property( !hw_gamma_write )
	// Linear to Gamma space
	outColour.xyz	= sqrt( finalColour );
@end @property( hw_gamma_write )
	outColour.xyz	= finalColour;
@end

@property( hlms_alphablend )
	// Fresnel-based transparency for realistic water
	float fresnelFactor = pow(1.0 - NdotV, 3.0);
	float baseAlpha = transparency;
	
	// More transparent at steep viewing angles, more opaque at grazing angles
	float finalAlpha = mix(baseAlpha, 1.0, fresnelFactor * 0.7);
	
	// Foam areas should be more opaque
	finalAlpha = mix(finalAlpha, 1.0, foam * 0.5);
	
	outColour.w = finalAlpha;
@end @property( !hlms_alphablend )
	outColour.w	= 1.0;@end

	@property( debug_pssm_splits )
		outColour.xyz = mix( outColour.xyz, debugPssmSplit.xyz, 0.2 );
	@end
	
	@insertpiece( custom_ps_posExecution )
}
@end