99 lines
3.2 KiB
Plaintext
99 lines
3.2 KiB
Plaintext
// Content pass of a HeightMapGeneratorLayer's shape.
|
|
//
|
|
// Writes one procedural shape's height contribution into the compositor's
|
|
// scratch buffer with blending disabled. The scratch buffer is then blended onto
|
|
// the height map by a plain BlitMaterial, which is where Add / Subtract / Replace
|
|
// actually happen - so this shader never reads the map and no ping-pong buffer is
|
|
// involved.
|
|
//
|
|
// Height lives in the R channel (G and B carry the same value so the map is
|
|
// readable as a greyscale preview), matching addTerrainOffset in
|
|
// Shading/Library/Terrain.gdshaderinc, which samples .r and maps 0..1 onto
|
|
// terrainMinHeight..terrainMaxHeight.
|
|
//
|
|
// Everything is evaluated in world XZ, not UV: HeightMapGenerator hands in the
|
|
// world-space rect this blit covers, so a shape that the map's edge clipped is
|
|
// still drawn at its true position and size. ( The TextureStack had a bug here
|
|
// once - clipping the rect while keeping rect-local UV silently rescaled the
|
|
// content. Deriving world space from the clipped rect cannot have that problem. )
|
|
|
|
shader_type texture_blit;
|
|
render_mode blend_disabled;
|
|
|
|
// World-space XZ rect this blit covers, from the (already clipped) pixel rect.
|
|
uniform vec2 shape_rect_world_min = vec2( 0.0, 0.0 );
|
|
uniform vec2 shape_rect_world_size = vec2( 1.0, 1.0 );
|
|
|
|
// HeightMapShapeType. Only Circle exists so far; the switch is the extension
|
|
// point for the primitives that follow.
|
|
uniform int shape_type = 0;
|
|
|
|
// HeightMapBlendMode - 0 Add, 1 Subtract, 2 Replace.
|
|
uniform int shape_blend_mode = 0;
|
|
|
|
// Normalized 0..1 height, already converted by the generator: a delta of the
|
|
// min-to-max range for Add/Subtract, an absolute level for Replace.
|
|
uniform float shape_value = 0.0;
|
|
|
|
uniform vec2 shape_center = vec2( 0.0, 0.0 );
|
|
uniform float shape_inner_radius = 0.0;
|
|
uniform float shape_outer_radius = 1.0;
|
|
uniform bool shape_smooth_falloff = true;
|
|
|
|
|
|
/** 1 inside the inner radius, falling to 0 at the outer radius. */
|
|
float heightMapShape_circle( vec2 worldXZ )
|
|
{
|
|
float distance = length( worldXZ - shape_center );
|
|
|
|
float inner = min( shape_inner_radius, shape_outer_radius );
|
|
float outer = max( shape_inner_radius, shape_outer_radius );
|
|
|
|
// A zero-width band is a hard edge, not a division by zero.
|
|
if ( outer <= inner )
|
|
{
|
|
return distance <= outer ? 1.0 : 0.0;
|
|
}
|
|
|
|
float t = clamp( ( distance - inner ) / ( outer - inner ), 0.0, 1.0 );
|
|
float weight = 1.0 - t;
|
|
|
|
if ( shape_smooth_falloff )
|
|
{
|
|
weight = weight * weight * ( 3.0 - 2.0 * weight );
|
|
}
|
|
|
|
return weight;
|
|
}
|
|
|
|
float heightMapShape_weight( vec2 worldXZ )
|
|
{
|
|
switch ( shape_type )
|
|
{
|
|
case 0: return heightMapShape_circle( worldXZ );
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
void blit()
|
|
{
|
|
vec2 worldXZ = shape_rect_world_min + UV * shape_rect_world_size;
|
|
|
|
float weight = heightMapShape_weight( worldXZ );
|
|
|
|
if ( shape_blend_mode == 2 )
|
|
{
|
|
// Replace: the height is flat and the falloff rides on alpha, so the Mix
|
|
// blend crossfades from the layer's height back to what is underneath.
|
|
COLOR0 = vec4( vec3( shape_value ), weight );
|
|
}
|
|
else
|
|
{
|
|
// Add / Subtract: alpha has to be 1, because the hardware blend multiplies
|
|
// the source by its own alpha before summing. The falloff is applied here
|
|
// instead.
|
|
COLOR0 = vec4( vec3( shape_value * weight ), 1.0 );
|
|
}
|
|
}
|