using System.Collections; using System.Collections.Generic; using Godot; using System; namespace Rokojori { public class TextureCombinerBuffer { int _width; public int width => _width; int _height; public int height => _height; Color[] _pixels; public int numPixels => _pixels.Length; public static TextureCombinerBuffer Create( int w, int h ) { var tb = new TextureCombinerBuffer(); tb._height = h; tb._width = w; tb._pixels = new Color[ w * h ]; return tb; } public void UnblendBlack( float treshold = 10f/255f ) { for ( int i = 0; i < _pixels.Length; i++ ) { _pixels[ i ] = _pixels[ i ].UnblendBlack( treshold ); } } public void Fill( Color color, int x, int y, int w, int h ) { for ( int i = 0; i < w; i++ ) { for ( int j = 0; j < h; j++ ) { SetAt( i + x, j + y, color ); } } } public void Fill( Color color ) { Fill( color, 0, 0, width, height ); } public static TextureCombinerBuffer From( Texture2D texture, int srgbConversion = 0 ) { var buffer = Create( texture.GetWidth(), texture.GetHeight() ); buffer.CopyFrom( texture.GetImage(), 0, 0, 0, 0, texture.GetWidth(), texture.GetHeight(), srgbConversion); return buffer; } public void CopyFromSampled( TextureCombinerBuffer source, int x, int y, int w, int h ) { // RJLog.Log( "Copy Sampled", x,y, w, h ); for ( int i = 0; i < w; i ++ ) { for ( int j = 0; j < h; j++ ) { float u = i / (float)( w ); float v = j / (float)( h ); var sourceX = Mathf.RoundToInt( u * source.width ); var sourceY = Mathf.RoundToInt( v * source.height ); var color = source.GetAt( sourceX, sourceY ); SetAt( x + i, y + j, color ); } } } public static Texture2D GridMerge( int w, int h, Color background, bool alpha, bool mipMaps, Vector2I alignment, List textures, List nullTextureColors ) { var positions = new List(); var sizes = new List(); var mergeSize = new Vector2( w, h ); var gridSize = (Vector2I)( mergeSize / alignment ); // RJLog.Log( "GridMerge" ); for ( int i = 0; i < textures.Count; i++ ) { var box = TextureMerger.GetUVAlignmentBoxFor( alignment, i ); var position = (Vector2I) ( box.min * mergeSize ); positions.Add( position ); sizes.Add( gridSize ); // RJLog.Log( i, position, gridSize ); } return Merge( w, h, background, alpha, mipMaps, textures, nullTextureColors, positions, sizes ); } public static Texture2D Merge( int w, int h, Color background, bool alpha, bool mipMaps, List textures, List nullTextureColors, List positions, List sizes ) { var buffer = Create( w, h ); buffer.Fill( background ); for ( int i = 0; i < textures.Count; i++ ) { var position = positions[ i ]; var size = sizes[ i ]; if ( textures[ i ] == null ) { buffer.Fill( nullTextureColors[ i ], position.X, position.Y, size.X, size.Y ); continue; } var imageBuffer = From( textures[ i ] ); buffer.CopyFromSampled( imageBuffer, position.X, position.Y, size.X, size.Y ); } return buffer.CreateImageTexture( alpha, mipMaps ); } public ImageTexture CreateImageTexture( bool alpha = true, bool generateMipmaps = false ) { var image = Image.CreateEmpty( width, height, generateMipmaps, alpha ? Image.Format.Rgba8 : Image.Format.Rgb8); CopyTo( 0, 0, 0, 0, width, height, image, alpha ); if ( generateMipmaps ) { image.GenerateMipmaps(); } return ImageTexture.CreateFromImage( image ); } 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, int srgbConversion = 0 ) { if ( srgbConversion != 0 ) { var gamma = Mathf.Pow( 2.2f, srgbConversion ); for ( int i = 0; i < w; i++ ) { for ( int j = 0; j < h; j++ ) { var sourcePixel = image == null ? new Color( 0, 0, 0 ) : image.GetPixel( sourceX + i, sourceY + j ); sourcePixel = sourcePixel.Gamma( gamma ); SetAt( ownX + i, ownY + j, sourcePixel ); } } } else { for ( int i = 0; i < w; i++ ) { for ( int j = 0; j < h; j++ ) { var sourcePixel = image == null ? new Color( 0, 0, 0 ) : 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, bool alpha = true ) { if ( ! alpha ) { for ( int i = 0; i < w; i++ ) { for ( int j = 0; j < h; j++ ) { var ownPixel = GetAt( ownX + i, ownY + j ); ownPixel.A = 1; image.SetPixel( targetX + i, targetY + j, ownPixel ); } } } else { 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 Vector2I ComputePositionFromIndex( int index ) { var x = index % width; var y = index / width; return new Vector2I( x, y ); } public void SetAt( int x, int y, Color value ) { SetIndexed( ComputeIndexFromPosition( x, y ), value ); } public void SetIndexed( int index, Color value ) { _pixels[ index ] = value; } public Color GetAt( int x, int y ) { return GetIndexed( ComputeIndexFromPosition( x, y ) ); } 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 ); } } }