From 8f2d50ff7157bd5f33f538d2dc65eb84b9cc17b5 Mon Sep 17 00:00:00 2001 From: Josef Date: Wed, 19 Feb 2025 13:12:12 +0100 Subject: [PATCH] Texture Combiner/Grass Updates --- Runtime/Colors/ColorX.cs | 99 ++++++++- Runtime/Procedural/Assets/Grass/GrassPatch.cs | 14 +- .../Assets/Grass/Windy Grass Shader.gdshader | 5 +- .../Layers/TextureCombinerTextureLayer.cs | 61 ++++++ .../TextureCombiner/TextureCombiner.cs | 198 +++++++++++++++++- .../TextureCombinerBlendMode.cs | 114 +++++++++- .../TextureCombiner/TextureCombinerBuffer.cs | 127 ++++++++++- .../TextureCombiner/TextureCombinerLayer.cs | 8 +- .../TextureCombinerMaterialOutput.cs | 21 ++ .../TextureCombinerProcessingRect.cs | 38 +++- .../TextureCombiner/TextureCombinerStack.cs | 17 ++ Runtime/Tools/Async.cs | 56 +++++ 12 files changed, 723 insertions(+), 35 deletions(-) create mode 100644 Runtime/Procedural/Textures/TextureCombiner/Layers/TextureCombinerTextureLayer.cs create mode 100644 Runtime/Procedural/Textures/TextureCombiner/TextureCombinerMaterialOutput.cs create mode 100644 Runtime/Procedural/Textures/TextureCombiner/TextureCombinerStack.cs create mode 100644 Runtime/Tools/Async.cs diff --git a/Runtime/Colors/ColorX.cs b/Runtime/Colors/ColorX.cs index 7293e69..268ea21 100644 --- a/Runtime/Colors/ColorX.cs +++ b/Runtime/Colors/ColorX.cs @@ -4,6 +4,8 @@ namespace Rokojori { public static class ColorX { + + public static float AlphaMultipliedR( this Color c ) { return c.R * c.A; @@ -17,22 +19,105 @@ namespace Rokojori public static float AlphaMultipliedB( this Color c ) { return c.B * c.A; + } + + public static float pmR( this Color c ) => c.AlphaMultipliedR(); + public static float pmG( this Color c ) => c.AlphaMultipliedG(); + public static float pmB( this Color c ) => c.AlphaMultipliedB(); + + static float Clamp( float f ) + { + return MathX.Clamp01( f ); } public static Color Blend( Color bottom, Color top ) { var fade = ( 1f - top.A ); - var a0 = top.A + bottom.A * fade; + var a = top.A + bottom.A * fade; - var xR = top.AlphaMultipliedR() + bottom.AlphaMultipliedR() * fade; - var xG = top.AlphaMultipliedG() + bottom.AlphaMultipliedG() * fade; - var xB = top.AlphaMultipliedB() + bottom.AlphaMultipliedB() * fade; + var r = top.pmR() + bottom.pmR() * fade; + var g = top.pmG() + bottom.pmG() * fade; + var b = top.pmB() + bottom.pmB() * fade; - var normalizer = 1f / a0; - - return new Color( xR, xG, xB, 1 ) * normalizer; + return new Color( r, g, b, 1 ) / a; } + public static Color PreMultipliedSingleComponentBlendMode( Color bottom, Color top, System.Func blending ) + { + var fade = ( 1f - top.A ); + var a = top.A + bottom.A * fade; + + var r = blending( bottom.pmR(), top.pmR(), fade ); + var g = blending( bottom.pmG(), top.pmG(), fade ); + var b = blending( bottom.pmB(), top.pmB(), fade ); + + return new Color( r, g, b, 1 ) / a; + } + + + + public static Color BlendMultiply( Color bottom, Color top ) + { + return PreMultipliedSingleComponentBlendMode( bottom, top, + ( a, b, f)=> + { + return a * b; + } + ); + } + + public static Color BlendScreen( Color bottom, Color top ) + { + return PreMultipliedSingleComponentBlendMode( bottom, top, + ( a, b, f)=> + { + return 1f - ( 1f - a ) * ( 1f - b); + } + ); + } + + public static Color BlendOverlay( Color bottom, Color top ) + { + return PreMultipliedSingleComponentBlendMode( bottom, top, + ( a, b, f)=> + { + return a < 0.5f ? (2.0f * a * b) : (1.0f - 2.0f * (1.0f - a) * (1.0f - b)); + } + ); + } + + public static Color BlendHardLight( Color bottom, Color top ) + { + return PreMultipliedSingleComponentBlendMode( bottom, top, + ( a, b, f)=> + { + return b < 0.5f ? (2.0f * a * b) : (1.0f - 2.0f * (1.0f - a) * (1.0f - b)) ; + } + ); + } + + public static Color BlendSoftLight( Color bottom, Color top ) + { + return PreMultipliedSingleComponentBlendMode( bottom, top, + ( a, b, f)=> + { + return b < 0.5f ? (2.0f * a * b) : (1.0f - 2.0f * (1.0f - a) * (1.0f - b)) ; + } + ); + } + + public static Color BlendAdd( Color bottom, Color top ) + { + return PreMultipliedSingleComponentBlendMode( bottom, top, + ( b, t, f)=> + { + return b + t; + } + ); + } + + + public static Color Lerp( Color a, Color b, float amount ) { return a.Lerp( b, amount ); diff --git a/Runtime/Procedural/Assets/Grass/GrassPatch.cs b/Runtime/Procedural/Assets/Grass/GrassPatch.cs index 77c71a3..6c2fa6a 100644 --- a/Runtime/Procedural/Assets/Grass/GrassPatch.cs +++ b/Runtime/Procedural/Assets/Grass/GrassPatch.cs @@ -133,6 +133,9 @@ namespace Rokojori [Export] public Curve rolling = MathX.Curve( 0f ); + [Export] + public Curve rolling2 = null; + [ExportGroup( "Blade Offset & Scale")] @@ -619,11 +622,18 @@ namespace Rokojori outputSpline.AutoOrientateByTangents( Vector3.Back ); + var rollingMix = random.Next(); + + var otherRolling = rolling2 == null ? rolling : rolling2; + for ( int i = 0; i < outputPoints.Count - 1; i++ ) { var t = i / (float)( outputPoints.Count - 2 ); - var rotation = rolling.Sample( t ); - RollSpline( outputPoints, i, rotation ); + var rotation = rolling.Sample( t ); + var rotation2 = otherRolling.Sample( t ); + var mixedRotation = Mathf.Lerp( rotation, rotation2, rollingMix ); + + RollSpline( outputPoints, i, mixedRotation ); } var modifier = new SplinesDeformModifier(); diff --git a/Runtime/Procedural/Assets/Grass/Windy Grass Shader.gdshader b/Runtime/Procedural/Assets/Grass/Windy Grass Shader.gdshader index 50a6062..932b440 100644 --- a/Runtime/Procedural/Assets/Grass/Windy Grass Shader.gdshader +++ b/Runtime/Procedural/Assets/Grass/Windy Grass Shader.gdshader @@ -53,8 +53,9 @@ void vertex() float strength = texture( windNoise, windUV + windNoiseStrengthOffset ).r * windStrength; vec2 circle = onCircle( angle ) * strength; VERTEX = worldToLocal( worldVertex + vec3( circle.x, 0, circle.y ) * windAmount, MODEL_MATRIX ); - VERTEX.y = mix( VERTEX.y, max( 0, VERTEX.y - strength * windAmount), windHeightCompensation * 2.0f ); - + float minY = min( VERTEX.y, 0 ); + // VERTEX.y = mix( VERTEX.y, max( 0, VERTEX.y - strength * windAmount), windHeightCompensation * 2.0f ); + VERTEX.y = mix( VERTEX.y, max( minY, VERTEX.y - strength * windAmount), windHeightCompensation * 4.0f ); } void fragment() diff --git a/Runtime/Procedural/Textures/TextureCombiner/Layers/TextureCombinerTextureLayer.cs b/Runtime/Procedural/Textures/TextureCombiner/Layers/TextureCombinerTextureLayer.cs new file mode 100644 index 0000000..088264b --- /dev/null +++ b/Runtime/Procedural/Textures/TextureCombiner/Layers/TextureCombinerTextureLayer.cs @@ -0,0 +1,61 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; +using System.Threading; +using System.Threading.Tasks; + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class TextureCombinerTextureLayer:TextureCombinerLayer + { + [Export] + public Texture2D texture; + + [Export] + public Vector2 tiling = Vector2.One; + + [Export] + public Vector2 offset = Vector2.Zero; + + [Export] + public bool updateTexture; + + TextureCombinerBuffer textureBuffer; + + public override async Task Process( TextureCombinerProcessingRect processingRect ) + { + var time = Async.StartTimer(); + + if ( updateTexture || textureBuffer == null ) + { + textureBuffer = TextureCombinerBuffer.From( texture ); + + time = await Async.WaitIfExceeded( time ); + } + + + for ( int i = 0; i < processingRect.processingWidth; i++ ) + { + for ( int j = 0; j < processingRect.processingHeight; j++ ) + { + var uv = processingRect.GetRelativeUV( i, j ); + + uv *= tiling; + uv += offset; + + uv = uv.PosMod( 1f ); + + var color = textureBuffer.SampleBilinearUV( uv ); + + processingRect.SetOutputRelative( i, j, color ); + } + } + + return; + } + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombiner.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombiner.cs index 801108a..5a6e6d8 100644 --- a/Runtime/Procedural/Textures/TextureCombiner/TextureCombiner.cs +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombiner.cs @@ -2,7 +2,8 @@ using System.Collections; using System.Collections.Generic; using Godot; using System; - +using System.Threading; +using System.Threading.Tasks; namespace Rokojori @@ -12,6 +13,199 @@ namespace Rokojori public partial class TextureCombiner:Resource { [Export] - public TextureCombinerLayer[] layers; + public Texture2D textureOutput; + + [Export] + public bool createMipmaps = true; + + [Export] + public TextureCombinerMaterialOutput materialOutput; + + [Export] + public bool create + { + get => false; + set { if ( ! value ) return; CreateTexture(); } + } + + + [Export] + public TextureCombinerStack layerStack; + + [Export] + public int width = 1024; + + [Export] + public int height = 1024; + + [ExportGroup( "Processing Settings")] + [Export] + bool _creatingTexture = false; + + [Export] + int _blockSize = 512; + + async void CreateTexture() + { + if ( _creatingTexture ) + { + return; + } + + var hRatio = width / (float)height; + var waitImage = Image.CreateEmpty( 32, Mathf.Max( 1, Mathf.RoundToInt( 32 / hRatio ) ), false, Image.Format.Rgba8 ); + waitImage.Fill( new Color( 1, 0, 1 )); + + textureOutput = ImageTexture.CreateFromImage( waitImage ); + + + _creatingTexture = true; + + var context = new TextureCombinerContext(); + context.combiner = this; + context.imageWidth = width; + context.imageHeight = height; + + outputBuffer = TextureCombinerBuffer.Create( width, height ); + inputBuffer = TextureCombinerBuffer.Create( width, height ); + + var first = true; + + for ( int i = 0; i < layerStack.layers.Length; i++ ) + { + var layerIndex = ( layerStack.layers.Length - 1 ) - i; + var layer = layerStack.layers[ layerIndex ]; + + if ( ! layer.visible ) + { + continue; + } + + await ProcessLayer( layer, context ); + + if ( first ) + { + var b = inputBuffer; + inputBuffer = outputBuffer; + outputBuffer = b; + + first = false; + } + else + { + await BlendLayer( layer, context ); + } + + } + + var image = Image.CreateEmpty( width, height, createMipmaps, Image.Format.Rgba8 ); + inputBuffer.CopyTo( 0, 0, 0, 0, width, height, image ); + image.GenerateMipmaps(); + + textureOutput = ImageTexture.CreateFromImage( image ); + + if ( materialOutput != null && + materialOutput.material != null && + materialOutput.textureName != null + ) + { + materialOutput.textureName.Set( materialOutput.material, textureOutput ); + } + + _creatingTexture = false; + } + + async Task ProcessLayer( TextureCombinerLayer layer, TextureCombinerContext context ) + { + var blockSize = _blockSize; + var blocksX = Mathf.CeilToInt( width / (float) blockSize ); + var blocksY = Mathf.CeilToInt( height / (float) blockSize ); + + var time = Async.StartTimer(); + + for ( int i = 0; i < blocksX; i++ ) + { + var startX = i * blockSize; + var endX = Mathf.Min( startX + blockSize, width ); + + for ( int j = 0; j < blocksY; j++ ) + { + var startY = j * blockSize; + var endY = Mathf.Min( startY + blockSize, height ); + + var rect = new TextureCombinerProcessingRect(); + + rect.processingX = startX; + rect.processingY = startY; + rect.processingWidth = endX - startX; + rect.processingHeight = endY - startY; + rect.context = context; + rect.inputBuffer = inputBuffer; + rect.outputBuffer = outputBuffer; + + await layer.Process( rect ); + + time = await Async.WaitIfExceeded( time ); + + } + } + } + + async Task BlendLayer( TextureCombinerLayer layer, TextureCombinerContext context ) + { + var blockSize = _blockSize; + var blocksX = Mathf.CeilToInt( width / (float) blockSize ); + var blocksY = Mathf.CeilToInt( height / (float) blockSize ); + + var time = Async.StartTimer(); + + TextureCombinerBuffer maskBuffer = null; + + if ( layer.opacityMask != null ) + { + maskBuffer = TextureCombinerBuffer.From( layer.opacityMask ).Resize( context.imageWidth, context.imageHeight ); + } + + for ( int i = 0; i < blocksX; i++ ) + { + var startX = i * blockSize; + var endX = Mathf.Min( startX + blockSize, width ); + + for ( int j = 0; j < blocksY; j++ ) + { + var startY = j * blockSize; + var endY = Mathf.Min( startY + blockSize, width ); + + var rect = new TextureCombinerProcessingRect(); + + if ( layer.opacityMask == null ) + { + TextureCombinerBlendModeAlgorithm.Blend( layer.blendMode, + startX, startY, endX - startX, endY - startY, layer.opacity, + inputBuffer, outputBuffer, inputBuffer + ); + } + else + { + TextureCombinerBlendModeAlgorithm.BlendMasked( layer.blendMode, + startX, startY, endX - startX, endY - startY, layer.opacity, maskBuffer, + inputBuffer, outputBuffer, inputBuffer + ); + } + + + time = await Async.WaitIfExceeded( time ); + + } + } + } + + + TextureCombinerBuffer outputBuffer; + TextureCombinerBuffer inputBuffer; + + + + } } \ No newline at end of file diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBlendMode.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBlendMode.cs index 8b65aa4..0c4a66f 100644 --- a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBlendMode.cs +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBlendMode.cs @@ -11,23 +11,53 @@ namespace Rokojori { Normal, Add, - Multiply + Multiply, + Screen, + Overlay, + HardLight, + SoftLight } public class TextureCombinerBlendModeAlgorithm { public static void Blend( TextureCombinerBlendMode blendMode, int x, int y, int w, int h, float topOpacity, - TextureBuffer bottom, TextureBuffer top, TextureBuffer output + TextureCombinerBuffer bottom, TextureCombinerBuffer top, TextureCombinerBuffer output ) { if ( TextureCombinerBlendMode.Normal == blendMode ) { - BlendNormal( x, y, w, h, topOpacity, bottom, top, output ); + BlendMode( ColorX.Blend, x, y, w, h, topOpacity, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Add == blendMode ) + { + BlendMode( ColorX.BlendAdd, x, y, w, h, topOpacity, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Multiply == blendMode ) + { + BlendMode( ColorX.BlendMultiply, x, y, w, h, topOpacity, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Screen == blendMode ) + { + BlendMode( ColorX.BlendScreen, x, y, w, h, topOpacity, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Overlay == blendMode ) + { + BlendMode( ColorX.BlendOverlay, x, y, w, h, topOpacity, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.HardLight == blendMode ) + { + BlendMode( ColorX.BlendHardLight, x, y, w, h, topOpacity, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.SoftLight == blendMode ) + { + BlendMode( ColorX.BlendSoftLight, x, y, w, h, topOpacity, bottom, top, output ); } } - static void BlendNormal( int x, int y, int w, int h, float topOpacity, - TextureBuffer bottom, TextureBuffer top, TextureBuffer output ) + + + static void BlendMode( System.Func blender, int x, int y, int w, int h, float topOpacity, + TextureCombinerBuffer bottom, TextureCombinerBuffer top, TextureCombinerBuffer output ) { for ( int i = 0; i < w; i++ ) { @@ -37,12 +67,80 @@ namespace Rokojori { var ry = y + j; - var index = output.ComputeIndexFromPosition( rx, ry ); + var index = output.ComputeIndexFromPosition( rx, ry ); - var topColor = top.GetIndexed( index ).FadeAlpha( topOpacity ); + var topColor = top.GetIndexed( index ); var bottomColor = bottom.GetIndexed( index ); - var outputColor = bottomColor.Blend( topColor ); + + var outputColor = blender( bottomColor, topColor ); + + outputColor = bottomColor.Blend( outputColor.FadeAlpha( topOpacity ) ); + + output.SetIndexed( index, outputColor ); + + } + } + } + + public static void BlendMasked( TextureCombinerBlendMode blendMode, int x, int y, int w, int h, float topOpacity, + TextureCombinerBuffer mask, + TextureCombinerBuffer bottom, TextureCombinerBuffer top, TextureCombinerBuffer output + ) + { + if ( TextureCombinerBlendMode.Normal == blendMode ) + { + BlendModeMasked( ColorX.Blend, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Add == blendMode ) + { + BlendModeMasked( ColorX.BlendAdd, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Multiply == blendMode ) + { + BlendModeMasked( ColorX.BlendMultiply, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Screen == blendMode ) + { + BlendModeMasked( ColorX.BlendScreen, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.Overlay == blendMode ) + { + BlendModeMasked( ColorX.BlendOverlay, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.HardLight == blendMode ) + { + BlendModeMasked( ColorX.BlendHardLight, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + else if ( TextureCombinerBlendMode.SoftLight == blendMode ) + { + BlendModeMasked( ColorX.BlendSoftLight, x, y, w, h, topOpacity, mask, bottom, top, output ); + } + } + + static void BlendModeMasked( System.Func blender, int x, int y, int w, int h, float topOpacity, + TextureCombinerBuffer mask, + TextureCombinerBuffer bottom, TextureCombinerBuffer top, TextureCombinerBuffer output ) + { + for ( int i = 0; i < w; i++ ) + { + var rx = x + i; + + for ( int j = 0; j < h; j++ ) + { + var ry = y + j; + + var index = output.ComputeIndexFromPosition( rx, ry ); + + var topColor = top.GetIndexed( index ); + var bottomColor = bottom.GetIndexed( index ); + + + var outputColor = blender( bottomColor, topColor ); + + var maskOpacity = mask.GetIndexed( index ); + + outputColor = bottomColor.Blend( outputColor.FadeAlpha( topOpacity * maskOpacity.R ) ); output.SetIndexed( index, outputColor ); diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs index 99ac6dd..b929d88 100644 --- a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerBuffer.cs @@ -7,7 +7,7 @@ using System; namespace Rokojori { - public class TextureBuffer + public class TextureCombinerBuffer { int _width; public int width => _width; @@ -15,43 +15,150 @@ namespace Rokojori int _height; public int height => _height; - T[] _pixels; + Color[] _pixels; - public static TextureBuffer Create( int w, int h ) + public static TextureCombinerBuffer Create( int w, int h ) { - var tb = new TextureBuffer(); + var tb = new TextureCombinerBuffer(); tb._height = h; tb._width = w; - tb._pixels = new T[ w * h ]; + tb._pixels = new Color[ w * h ]; return tb; } + public static TextureCombinerBuffer From( Texture2D texture ) + { + var buffer = Create( texture.GetWidth(), texture.GetHeight() ); + buffer.CopyFrom( texture.GetImage(), 0, 0, 0, 0, texture.GetWidth(), texture.GetHeight() ); + + return buffer; + } + + public TextureCombinerBuffer Resize( int w, int h ) + { + var buffer = Create( w, h ); + + for ( int i = 0; i < w; i++ ) + { + for ( int j = 0; j < h; j++ ) + { + var uv = new Vector2( i / (float) w, j / (float) h ); + var pixel = SampleBilinearUV( uv ); + + buffer.SetAt( i, j, pixel ); + } + } + + return buffer; + + } + + public void CopyFrom( Image image, int sourceX, int sourceY, int ownX, int ownY, int w, int h ) + { + for ( int i = 0; i < w; i++ ) + { + for ( int j = 0; j < h; j++ ) + { + var sourcePixel = image.GetPixel( sourceX + i, sourceY + j ); + + SetAt( ownX + i, ownY + j, sourcePixel ); + + } + } + } + + + public void CopyTo( int ownX, int ownY, int targetX, int targetY, int w, int h, TextureCombinerBuffer output ) + { + for ( int i = 0; i < w; i++ ) + { + for ( int j = 0; j < h; j++ ) + { + var ownPixel = GetAt( ownX + i, ownY + j ); + + output.SetAt( targetX + i, targetY + j, ownPixel ); + + } + } + } + + public void CopyTo( int ownX, int ownY, int targetX, int targetY, int w, int h, Image image ) + { + for ( int i = 0; i < w; i++ ) + { + for ( int j = 0; j < h; j++ ) + { + var ownPixel = GetAt( ownX + i, ownY + j ); + image.SetPixel( targetX + i, targetY + j, ownPixel ); + + } + } + } + public int ComputeIndexFromPosition( int x, int y ) { return x + y * _width; } - public void SetAt( int x, int y, T value ) + public void SetAt( int x, int y, Color value ) { SetIndexed( ComputeIndexFromPosition( x, y ), value ); } - - public void SetIndexed( int index, T value ) + + public void SetIndexed( int index, Color value ) { _pixels[ index ] = value; } - public T GetAt( int x, int y ) + public Color GetAt( int x, int y ) { return GetIndexed( ComputeIndexFromPosition( x, y ) ); } - public T GetIndexed( int index ) + public Color GetIndexed( int index ) { return _pixels[ index ]; + } + + public Color SampleNearestUV( Vector2 uv ) + { + return SampleNearestImage( uv.X * width, uv.Y * height ); } + public Color SampleNearestImage( float imageDimensionsX, float imageDimensionsY ) + { + return GetAt( Mathf.RoundToInt( imageDimensionsX ), Mathf.RoundToInt( imageDimensionsY ) ); + } + + public Color SampleBilinearUV( Vector2 uv ) + { + return SampleBilinearImage( uv.X * width, uv.Y * height ); + } + + public Color SampleBilinearImage( float imageDimensionsX, float imageDimensionsY ) + { + var lowX = Mathf.FloorToInt( imageDimensionsX ); + var highX = Mathf.Min( lowX + 1, width - 1 ); + var lerpX = imageDimensionsX - lowX; + + + var lowY = Mathf.FloorToInt( imageDimensionsY ); + var highY = Mathf.Min( lowY + 1, height - 1 ); + var lerpY = imageDimensionsY - lowY; + + + var ll = GetAt( lowX, lowY ); + var hl = GetAt( highX, lowY ); + + var lh = GetAt( lowX, highY ); + var hh = GetAt( highX, highY ); + + var upper = ll.Lerp( hl, lerpX ); + var lower = lh.Lerp( hh, lerpX ); + + return upper.Lerp( lower, lerpY ); + } } } \ No newline at end of file diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerLayer.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerLayer.cs index 99ae94d..02b61c6 100644 --- a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerLayer.cs +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerLayer.cs @@ -18,8 +18,14 @@ namespace Rokojori [Export( PropertyHint.Range, "0,1")] public float opacity = 1; + + [Export] + public Texture2D opacityMask; + + [Export] + public bool visible = true; - public async Task Process( TextureCombinerProcessingRect processingRect ) + public virtual async Task Process( TextureCombinerProcessingRect processingRect ) { return; } diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerMaterialOutput.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerMaterialOutput.cs new file mode 100644 index 0000000..d279381 --- /dev/null +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerMaterialOutput.cs @@ -0,0 +1,21 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class TextureCombinerMaterialOutput:Resource + { + [Export] + public Material material; + + [Export] + public Texture2DPropertyName textureName; + + } +} \ No newline at end of file diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerProcessingRect.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerProcessingRect.cs index 2095eae..46e57b0 100644 --- a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerProcessingRect.cs +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerProcessingRect.cs @@ -2,7 +2,8 @@ using System.Collections; using System.Collections.Generic; using Godot; using System; - +using System.Threading; +using System.Threading.Tasks; namespace Rokojori @@ -12,10 +13,41 @@ namespace Rokojori public TextureCombinerContext context; public int processingX = 0; public int processingY = 0; - public int processingWidht = 0; + public int processingWidth = 0; public int processingHeight = 0; - public TextureBuffer output; + + public Vector2 GetUV( int x, int y ) + { + return new Vector2( x / (float) context.imageWidth, y / (float) context.imageHeight ); + } + + public Vector2 GetRelativeUV( int x, int y ) + { + return GetUV( x + processingX, y + processingY ); + } + + public void SetOutputRelative( int x, int y, Color c ) + { + outputBuffer.SetAt( x + processingX, y + processingY, c ); + } + + + public Vector2 uvStart => GetUV( processingX, processingY ); + public Vector2 uvEnd => GetUV( processingX + processingWidth, processingY + processingHeight ); + + public TextureCombinerBuffer inputBuffer; + public TextureCombinerBuffer outputBuffer; + + + public async Task CopyToFinalOutput( TextureCombinerBuffer bottomBuffer, TextureCombinerBuffer image ) + { + var time = Async.StartTimer(); + bottomBuffer.CopyTo( 0, 0, processingX, processingY, processingWidth, processingHeight, image ); + time = await Async.WaitIfExceeded( time ); + + return; + } } } \ No newline at end of file diff --git a/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerStack.cs b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerStack.cs new file mode 100644 index 0000000..efb947a --- /dev/null +++ b/Runtime/Procedural/Textures/TextureCombiner/TextureCombinerStack.cs @@ -0,0 +1,17 @@ +using System.Collections; +using System.Collections.Generic; +using Godot; +using System; + + + +namespace Rokojori +{ + [Tool] + [GlobalClass] + public partial class TextureCombinerStack:Resource + { + [Export] + public TextureCombinerLayer[] layers; + } +} \ No newline at end of file diff --git a/Runtime/Tools/Async.cs b/Runtime/Tools/Async.cs new file mode 100644 index 0000000..62ee7c2 --- /dev/null +++ b/Runtime/Tools/Async.cs @@ -0,0 +1,56 @@ +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System; +using Godot; + +using System; +using System.Threading.Tasks; + +namespace Rokojori +{ + + public class Async + { + public const double waitTime = 1.0 / 60.0; + + public static async Task Wait( double waitTime = Async.waitTime ) + { + await Task.Delay( (int)( waitTime * 1000 ) ); + return; + } + + public static double StartTimer() + { + return Time.GetTicksMsec() / 1000.0; + } + + public static async Task WaitIfExceeded( double last, double waitTime = Async.waitTime ) + { + var now = Time.GetTicksMsec() / 1000.0; + + if ( ( now - last ) > waitTime ) + { + await Task.Delay( (int)( waitTime * 1000 ) ); + + return Time.GetTicksMsec() / 1000.0;; + } + + return last; + } + + public static double DoIfExceeded( double last, System.Action action, double waitTime = Async.waitTime ) + { + var now = Time.GetTicksMsec() / 1000.0; + + if ( ( now - last ) > waitTime ) + { + action(); + } + + + return Time.GetTicksMsec() / 1000.0;; + } + } + +} \ No newline at end of file