Texture Combiner/Grass Updates
This commit is contained in:
parent
756b1d2a9d
commit
8f2d50ff71
|
@ -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<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 )
|
||||
{
|
||||
return a.Lerp( b, amount );
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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<Color> bottom, TextureBuffer<Color> top, TextureBuffer<Color> 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<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++ )
|
||||
{
|
||||
|
@ -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<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 );
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ using System;
|
|||
|
||||
namespace Rokojori
|
||||
{
|
||||
public class TextureBuffer<T>
|
||||
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<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._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 );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
|
@ -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<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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue