// The mesh half of a spline height map layer - a G-buffer for a ribbon.
//
// This runs inside HeightMapMeshAtlas's SubViewport, on the ribbon mesh that
// HeightMapRibbonBuilder generates, and writes nothing but the ribbon's
// parametrization. The height itself is looked up later, in the blit, out of
// the texture atlas this buffer's UV points into - which is the whole reason
// the pass exists in this shape:
//
// - overlapping ribbons resolve by the depth test, because the
// mesh is real 3D geometry whose Z is the ribbon's own height. The highest
// fragment wins, with no blending and no accumulation pass, which is the one
// thing the blit path cannot do - there is no Max among the native blit
// blend modes.
// - a ribbon's UV comes off the mesh, so a start / middle / end texture is
// just three spans of the same strip pointing into three atlas slots. No
// branching per texel and no limit of three.
// - changing a texture does not invalidate this buffer at all, since the
// lookup is not in here.
//
// Channels, read back by heightMapShape_ribbon in HeightMapShape.gdshader:
//
// R, G - UV in the texture atlas, slot offset already baked in
// B - 0.5 + 0.5 * W, where W runs 0..1 over the whole
// spline, for effects that need to know where along the line they are
// A - 1, and not usable: an opaque spatial material cannot write alpha
//
// B carries the +0.5 bias so it also works as the stencil. The buffer is
// cleared transparent black, so B == 0 means "no ribbon here", and biasing W
// keeps every written value inside 0..1 - which an unbiased 1 + W would not,
// and a low dynamic range render target would then clamp it away.
//
// Unshaded and opaque on purpose. Opaque because a transparent material in
// Godot blends its colour by its own alpha and skips the depth write, and both
// of those would destroy a G-buffer; unshaded because ALBEDO then reaches the
// render target untouched by any lighting.
shader_type spatial;
render_mode unshaded, cull_disabled, depth_draw_opaque,
shadows_disabled, ambient_light_disabled, fog_disabled;
// The ribbon's own region in the atlas, as x, y, width, height in texels.
//
// Not a nicety: a layer's texel rect is clipped to the map, so a spline running
// off the map's edge has geometry outside the region its rect got - and the
// atlas packs other layers right next to it. Without this clip, a road leaving
// the map would overwrite the neighbouring layer's region.
//
// One material per ribbon rather than an instance uniform, because a material
// is a proven call shape here and there are only ever a handful of them.
uniform vec4 ribbon_region = vec4( 0.0, 0.0, 1.0, 1.0 );
// Atlas texel position, reconstructed from the vertex position - which is in
// atlas texels by construction, with Y negated so image row 0 ends up at the
// top of the camera's view. Interpolating it is exact, since the mapping from
// here to the render target is 1:1 and affine.
varying vec2 atlas_texel;
void vertex()
{
atlas_texel = vec2( VERTEX.x, -VERTEX.y );
}
void fragment()
{
vec2 from = atlas_texel - ribbon_region.xy;
if ( from.x < 0.0 || from.y < 0.0 || from.x > ribbon_region.z || from.y > ribbon_region.w )
{
discard;
}
ALBEDO = vec3( UV, 0.5 + 0.5 * UV2.x );
}