211 lines
5.3 KiB
C#
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;
|
|
|
|
|
|
|
|
|
|
}
|
|
} |