rj-action-library/Runtime/Procedural/Textures/TextureCombiner/TextureCombiner.cs

211 lines
5.3 KiB
C#

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 TextureCombiner:Resource
{
[Export]
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;
}
}