using Godot; using System; using Rokojori.Extensions; namespace Rokojori { /** Which primitive HeightMapShape.gdshader draws. Int values are switched on in the shader - do not renumber. */ public enum HeightMapShapeType { Circle = 0, Image = 1 } public class HeightMapShapeShader { public static readonly CachedResource shader = new CachedResource( "res://addons/rokojori_action_library/Runtime/Procedural/HeightMaps/Shaders/HeightMapShape.gdshader" ); public static readonly Vector2PropertyName rectWorldMin = Vector2PropertyName.Create( "shape_rect_world_min" ); public static readonly Vector2PropertyName rectWorldSize = Vector2PropertyName.Create( "shape_rect_world_size" ); public static readonly IntPropertyName shapeType = IntPropertyName.Create( "shape_type" ); public static readonly IntPropertyName blendMode = IntPropertyName.Create( "shape_blend_mode" ); public static readonly FloatPropertyName valueBase = FloatPropertyName.Create( "shape_value_base" ); public static readonly FloatPropertyName valueScale = FloatPropertyName.Create( "shape_value_scale" ); public static readonly Vector2PropertyName center = Vector2PropertyName.Create( "shape_center" ); public static readonly FloatPropertyName innerRadius = FloatPropertyName.Create( "shape_inner_radius" ); public static readonly FloatPropertyName outerRadius = FloatPropertyName.Create( "shape_outer_radius" ); public static readonly BoolPropertyName smoothFalloff = BoolPropertyName.Create( "shape_smooth_falloff" ); public static readonly Vector2PropertyName axisX = Vector2PropertyName.Create( "shape_axis_x" ); public static readonly Vector2PropertyName axisZ = Vector2PropertyName.Create( "shape_axis_z" ); public static readonly Vector2PropertyName imageSize = Vector2PropertyName.Create( "shape_size" ); public static readonly IntPropertyName imageChannel = IntPropertyName.Create( "shape_image_channel" ); public static readonly BoolPropertyName imageTiling = BoolPropertyName.Create( "shape_image_tiling" ); public static readonly IntPropertyName maskChannel = IntPropertyName.Create( "shape_mask_channel" ); public static readonly Vector2PropertyName maskCenter = Vector2PropertyName.Create( "shape_mask_center" ); public static readonly Vector2PropertyName maskAxisX = Vector2PropertyName.Create( "shape_mask_axis_x" ); public static readonly Vector2PropertyName maskAxisZ = Vector2PropertyName.Create( "shape_mask_axis_z" ); public static readonly Vector2PropertyName maskSize = Vector2PropertyName.Create( "shape_mask_size" ); } /** Material for HeightMapShape.gdshader. One instance per HeightMapGenerator, re-set per layer. The TextureStack's session-2 live test already proved that re-setting a shared blit material between queued blits is safe - the earlier blits do not pick up the later values - which is why every layer can share one material instead of allocating its own. The image texture is not a property here: it arrives as the blit's source, which is what hint_blit_source0 binds to. */ [Tool] public partial class HeightMapShapeMaterial:CustomMaterial { public readonly CustomMaterialProperty rectWorldMin; public readonly CustomMaterialProperty rectWorldSize; public readonly CustomMaterialProperty shapeType; public readonly CustomMaterialProperty blendMode; public readonly CustomMaterialProperty valueBase; public readonly CustomMaterialProperty valueScale; public readonly CustomMaterialProperty center; public readonly CustomMaterialProperty innerRadius; public readonly CustomMaterialProperty outerRadius; public readonly CustomMaterialProperty smoothFalloff; public readonly CustomMaterialProperty axisX; public readonly CustomMaterialProperty axisZ; public readonly CustomMaterialProperty imageSize; public readonly CustomMaterialProperty imageChannel; public readonly CustomMaterialProperty imageTiling; public readonly CustomMaterialProperty maskChannel; public readonly CustomMaterialProperty maskCenter; public readonly CustomMaterialProperty maskAxisX; public readonly CustomMaterialProperty maskAxisZ; public readonly CustomMaterialProperty maskSize; public HeightMapShapeMaterial() { Shader = HeightMapShapeShader.shader.Get(); rectWorldMin = new CustomMaterialProperty( this, HeightMapShapeShader.rectWorldMin ); rectWorldSize = new CustomMaterialProperty( this, HeightMapShapeShader.rectWorldSize ); shapeType = new CustomMaterialProperty( this, HeightMapShapeShader.shapeType ); blendMode = new CustomMaterialProperty( this, HeightMapShapeShader.blendMode ); valueBase = new CustomMaterialProperty( this, HeightMapShapeShader.valueBase ); valueScale = new CustomMaterialProperty( this, HeightMapShapeShader.valueScale ); center = new CustomMaterialProperty( this, HeightMapShapeShader.center ); innerRadius = new CustomMaterialProperty( this, HeightMapShapeShader.innerRadius ); outerRadius = new CustomMaterialProperty( this, HeightMapShapeShader.outerRadius ); smoothFalloff = new CustomMaterialProperty( this, HeightMapShapeShader.smoothFalloff ); axisX = new CustomMaterialProperty( this, HeightMapShapeShader.axisX ); axisZ = new CustomMaterialProperty( this, HeightMapShapeShader.axisZ ); imageSize = new CustomMaterialProperty( this, HeightMapShapeShader.imageSize ); imageChannel = new CustomMaterialProperty( this, HeightMapShapeShader.imageChannel ); imageTiling = new CustomMaterialProperty( this, HeightMapShapeShader.imageTiling ); maskChannel = new CustomMaterialProperty( this, HeightMapShapeShader.maskChannel ); maskCenter = new CustomMaterialProperty( this, HeightMapShapeShader.maskCenter ); maskAxisX = new CustomMaterialProperty( this, HeightMapShapeShader.maskAxisX ); maskAxisZ = new CustomMaterialProperty( this, HeightMapShapeShader.maskAxisZ ); maskSize = new CustomMaterialProperty( this, HeightMapShapeShader.maskSize ); } /** The mask rect, for a layer that has no opinion about it: anything the mask's own rect does not cover takes its border value, so a 1x1 rect centered on the shape is a flat multiplier over the whole shape. */ public void SetMaskRect( Vector2 center, Vector2 axisX, Vector2 axisZ, Vector2 size ) { maskCenter.Set( center ); maskAxisX.Set( axisX ); maskAxisZ.Set( axisZ ); maskSize.Set( size ); } /** Back to a shape that contributes nothing, so a layer that only sets some of the uniforms does not inherit the previous layer's. The rect uniforms are deliberately not reset - HeightMapGenerator.DrawShape owns those and sets them after the layer has configured the shape. */ public void Reset() { shapeType.Set( (int) HeightMapShapeType.Circle ); blendMode.Set( (int) HeightMapBlendMode.Add ); valueBase.Set( 0 ); valueScale.Set( 0 ); center.Set( Vector2.Zero ); innerRadius.Set( 0 ); outerRadius.Set( 0 ); smoothFalloff.Set( true ); axisX.Set( new Vector2( 1, 0 ) ); axisZ.Set( new Vector2( 0, 1 ) ); imageSize.Set( Vector2.One ); imageChannel.Set( (int) HeightMapChannel.R ); imageTiling.Set( false ); maskChannel.Set( (int) HeightMapChannel.A ); SetMaskRect( Vector2.Zero, new Vector2( 1, 0 ), new Vector2( 0, 1 ), Vector2.One ); } } }