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