Texture Combiner/Grass Updates

This commit is contained in:
Josef 2025-02-19 13:12:12 +01:00
parent 756b1d2a9d
commit 8f2d50ff71
12 changed files with 723 additions and 35 deletions

View File

@ -4,6 +4,8 @@ namespace Rokojori
{ {
public static class ColorX public static class ColorX
{ {
public static float AlphaMultipliedR( this Color c ) public static float AlphaMultipliedR( this Color c )
{ {
return c.R * c.A; return c.R * c.A;
@ -19,20 +21,103 @@ namespace Rokojori
return c.B * c.A; 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 ) public static Color Blend( Color bottom, Color top )
{ {
var fade = ( 1f - top.A ); 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 r = top.pmR() + bottom.pmR() * fade;
var xG = top.AlphaMultipliedG() + bottom.AlphaMultipliedG() * fade; var g = top.pmG() + bottom.pmG() * fade;
var xB = top.AlphaMultipliedB() + bottom.AlphaMultipliedB() * fade; var b = top.pmB() + bottom.pmB() * fade;
var normalizer = 1f / a0; return new Color( r, g, b, 1 ) / a;
return new Color( xR, xG, xB, 1 ) * normalizer;
} }
public static Color PreMultipliedSingleComponentBlendMode( Color bottom, Color top, System.Func<float,float,float, float> 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 ) public static Color Lerp( Color a, Color b, float amount )
{ {
return a.Lerp( b, amount ); return a.Lerp( b, amount );

View File

@ -133,6 +133,9 @@ namespace Rokojori
[Export] [Export]
public Curve rolling = MathX.Curve( 0f ); public Curve rolling = MathX.Curve( 0f );
[Export]
public Curve rolling2 = null;
[ExportGroup( "Blade Offset & Scale")] [ExportGroup( "Blade Offset & Scale")]
@ -619,11 +622,18 @@ namespace Rokojori
outputSpline.AutoOrientateByTangents( Vector3.Back ); outputSpline.AutoOrientateByTangents( Vector3.Back );
var rollingMix = random.Next();
var otherRolling = rolling2 == null ? rolling : rolling2;
for ( int i = 0; i < outputPoints.Count - 1; i++ ) for ( int i = 0; i < outputPoints.Count - 1; i++ )
{ {
var t = i / (float)( outputPoints.Count - 2 ); var t = i / (float)( outputPoints.Count - 2 );
var rotation = rolling.Sample( t ); var rotation = rolling.Sample( t );
RollSpline( outputPoints, i, rotation ); var rotation2 = otherRolling.Sample( t );
var mixedRotation = Mathf.Lerp( rotation, rotation2, rollingMix );
RollSpline( outputPoints, i, mixedRotation );
} }
var modifier = new SplinesDeformModifier(); var modifier = new SplinesDeformModifier();

View File

@ -53,8 +53,9 @@ void vertex()
float strength = texture( windNoise, windUV + windNoiseStrengthOffset ).r * windStrength; float strength = texture( windNoise, windUV + windNoiseStrengthOffset ).r * windStrength;
vec2 circle = onCircle( angle ) * strength; vec2 circle = onCircle( angle ) * strength;
VERTEX = worldToLocal( worldVertex + vec3( circle.x, 0, circle.y ) * windAmount, MODEL_MATRIX ); 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() void fragment()

View File

@ -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;
}
}
}

View File

@ -2,7 +2,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Godot; using Godot;
using System; using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori namespace Rokojori
@ -12,6 +13,199 @@ namespace Rokojori
public partial class TextureCombiner:Resource public partial class TextureCombiner:Resource
{ {
[Export] [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;
} }
} }

View File

@ -11,23 +11,53 @@ namespace Rokojori
{ {
Normal, Normal,
Add, Add,
Multiply Multiply,
Screen,
Overlay,
HardLight,
SoftLight
} }
public class TextureCombinerBlendModeAlgorithm public class TextureCombinerBlendModeAlgorithm
{ {
public static void Blend( TextureCombinerBlendMode blendMode, int x, int y, int w, int h, float topOpacity, public static void Blend( TextureCombinerBlendMode blendMode, int x, int y, int w, int h, float topOpacity,
TextureBuffer<Color> bottom, TextureBuffer<Color> top, TextureBuffer<Color> output TextureCombinerBuffer bottom, TextureCombinerBuffer top, TextureCombinerBuffer output
) )
{ {
if ( TextureCombinerBlendMode.Normal == blendMode ) 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<Color> bottom, TextureBuffer<Color> top, TextureBuffer<Color> output )
static void BlendMode( System.Func<Color,Color,Color> blender, int x, int y, int w, int h, float topOpacity,
TextureCombinerBuffer bottom, TextureCombinerBuffer top, TextureCombinerBuffer output )
{ {
for ( int i = 0; i < w; i++ ) for ( int i = 0; i < w; i++ )
{ {
@ -37,12 +67,80 @@ namespace Rokojori
{ {
var ry = y + j; 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 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<Color,Color,Color> 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 ); output.SetIndexed( index, outputColor );

View File

@ -7,7 +7,7 @@ using System;
namespace Rokojori namespace Rokojori
{ {
public class TextureBuffer<T> public class TextureCombinerBuffer
{ {
int _width; int _width;
public int width => _width; public int width => _width;
@ -15,43 +15,150 @@ namespace Rokojori
int _height; int _height;
public int height => _height; public int height => _height;
T[] _pixels; Color[] _pixels;
public static TextureBuffer<T> Create( int w, int h ) public static TextureCombinerBuffer Create( int w, int h )
{ {
var tb = new TextureBuffer<T>(); var tb = new TextureCombinerBuffer();
tb._height = h; tb._height = h;
tb._width = w; tb._width = w;
tb._pixels = new T[ w * h ]; tb._pixels = new Color[ w * h ];
return tb; 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 ) public int ComputeIndexFromPosition( int x, int y )
{ {
return x + y * _width; 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 ); SetIndexed( ComputeIndexFromPosition( x, y ), value );
} }
public void SetIndexed( int index, T value ) public void SetIndexed( int index, Color value )
{ {
_pixels[ index ] = value; _pixels[ index ] = value;
} }
public T GetAt( int x, int y ) public Color GetAt( int x, int y )
{ {
return GetIndexed( ComputeIndexFromPosition( x, y ) ); return GetIndexed( ComputeIndexFromPosition( x, y ) );
} }
public T GetIndexed( int index ) public Color GetIndexed( int index )
{ {
return _pixels[ 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 );
}
} }
} }

View File

@ -19,7 +19,13 @@ namespace Rokojori
[Export( PropertyHint.Range, "0,1")] [Export( PropertyHint.Range, "0,1")]
public float opacity = 1; public float opacity = 1;
public async Task Process( TextureCombinerProcessingRect processingRect ) [Export]
public Texture2D opacityMask;
[Export]
public bool visible = true;
public virtual async Task Process( TextureCombinerProcessingRect processingRect )
{ {
return; return;
} }

View File

@ -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;
}
}

View File

@ -2,7 +2,8 @@ using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Godot; using Godot;
using System; using System;
using System.Threading;
using System.Threading.Tasks;
namespace Rokojori namespace Rokojori
@ -12,10 +13,41 @@ namespace Rokojori
public TextureCombinerContext context; public TextureCombinerContext context;
public int processingX = 0; public int processingX = 0;
public int processingY = 0; public int processingY = 0;
public int processingWidht = 0; public int processingWidth = 0;
public int processingHeight = 0; public int processingHeight = 0;
public TextureBuffer<Color> 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;
}
} }
} }

View File

@ -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;
}
}

56
Runtime/Tools/Async.cs Normal file
View File

@ -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<double> 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;;
}
}
}