250 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			6.4 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 bool autoCreate = false;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public TextureCombinerStack layerStack;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public int width = 1024;
 | 
						|
    
 | 
						|
    [Export]
 | 
						|
    public int height = 1024;
 | 
						|
 | 
						|
    [ExportGroup( "Processing Settings")]
 | 
						|
    [Export]
 | 
						|
    public bool _creatingTexture = false;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    int _blockSize = 512;
 | 
						|
 | 
						|
    public void UpdateProgressTexture()
 | 
						|
    {
 | 
						|
      var size = 64;
 | 
						|
      var hRatio = width / (float)height;
 | 
						|
      var oProgress = Mathf.Min( 1, _progress * _progress + 0.2f );
 | 
						|
      var waitImage = Image.CreateEmpty( size, Mathf.Max( 1, Mathf.RoundToInt( size / hRatio ) ), false, Image.Format.Rgba8 );
 | 
						|
      waitImage.Fill( new HSLColor( oProgress * 120, 1f, 0.3f ) );
 | 
						|
 | 
						|
      var progressI = size * _progress;
 | 
						|
 | 
						|
      for ( int i = 0; i < progressI; i++ )
 | 
						|
      {
 | 
						|
        waitImage.SetPixel( i, waitImage.GetHeight() / 2 - 2,  new Color( 1, 1, 1 ) );
 | 
						|
        waitImage.SetPixel( ( size - 1 ) - i, waitImage.GetHeight() / 2 + 2,  new Color( 1, 1, 1 ) );
 | 
						|
      }
 | 
						|
    
 | 
						|
 | 
						|
      textureOutput = ImageTexture.CreateFromImage( waitImage );
 | 
						|
    }
 | 
						|
 | 
						|
    float _progress = 0;
 | 
						|
 | 
						|
    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 );
 | 
						|
      */
 | 
						|
 | 
						|
      UpdateProgressTexture();
 | 
						|
 | 
						|
    
 | 
						|
 | 
						|
      _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;
 | 
						|
 | 
						|
      var pNormalizer = 1f / ( layerStack.layers.Length - 2 );
 | 
						|
 | 
						|
      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;
 | 
						|
        }
 | 
						|
 | 
						|
        _progress = Mathf.Min( 1, i * pNormalizer );
 | 
						|
        UpdateProgressTexture();
 | 
						|
 | 
						|
        await ProcessLayer( layer, context );
 | 
						|
 | 
						|
        
 | 
						|
        _progress = Mathf.Min( 1, ( i + 0.5f ) * pNormalizer );
 | 
						|
        UpdateProgressTexture();
 | 
						|
 | 
						|
        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;
 | 
						|
 | 
						|
   
 | 
						|
 | 
						|
 | 
						|
  }
 | 
						|
} |