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

317 lines
8.1 KiB
C#

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<Texture2D> textures, List<Color> nullTextureColors )
{
var positions = new List<Vector2I>();
var sizes = new List<Vector2I>();
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<Texture2D> textures, List<Color> nullTextureColors, List<Vector2I> positions, List<Vector2I> 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 );
}
}
}