using System; using Godot; using System.Collections.Generic; using System.Text; namespace Rokojori { public class RDTextureFormats { public enum PixelChannelEncoding { Unknown, uInt8, sInt8, uInt16, sInt16, sFloat16, uNorm16, sNorm16, uInt32, sInt32, sFloat32 } public static readonly List bits8 = new List() { PixelChannelEncoding.uInt8, PixelChannelEncoding.sInt8 }; public static readonly List bits32 = new List() { PixelChannelEncoding.uInt32, PixelChannelEncoding.sInt32, PixelChannelEncoding.sFloat32 }; public static int NumBytesFor( PixelChannelEncoding encoding ) { if ( PixelChannelEncoding.Unknown == encoding ) { return -1; } if ( bits8.Contains( encoding ) ) { return 1; } if ( bits32.Contains( encoding ) ) { return 4; } return 2; } public static string UsageInfo( RenderingDevice.TextureUsageBits bits ) { var enums = Enum.GetValues(); var sb = new StringBuilder(); var first = true; Array.ForEach( enums, e => { if ( ! BitMath.IsMaskSet( (long)bits, (long)e ) ) { return; } if ( first ){ first = false; } else { sb.Append( ", " ); } sb.Append( e + "" ); } ); return sb.ToString(); } public const RenderingDevice.TextureUsageBits Usage_Default = RenderingDevice.TextureUsageBits.SamplingBit | RenderingDevice.TextureUsageBits.StorageBit | RenderingDevice.TextureUsageBits.CpuReadBit | RenderingDevice.TextureUsageBits.CanUpdateBit | RenderingDevice.TextureUsageBits.CanCopyFromBit | RenderingDevice.TextureUsageBits.CanCopyToBit; public static RDTextureFormat DefaultFormat( int w, int h, RenderingDevice.DataFormat dataFormat = RenderingDevice.DataFormat.R16G16B16A16Unorm ) { var format = new RDTextureFormat(); format.Width = (uint) w; format.Height = (uint) h; format.Format = dataFormat; format.UsageBits = Usage_Default; return format; } public static RDTextureFormat FormatChangeSize( RDTextureFormat tf, Vector2I size ) { return FormatChangeSize( tf, size.X, size.Y ); } public static RDTextureFormat FormatChangeSize( RDTextureFormat tf, int w, int h ) { var clone = FormatCopy( tf ); clone.Width = (uint) w; clone.Height = (uint) h; return clone; } public static int GetNumPixels( SubViewport viewport ) { return viewport.Size.X * viewport.Size.Y; } public static int GetNumBytes( SubViewport viewport ) { var numChannels = viewport.TransparentBg ? 4 : 3; var encoding = viewport.UseHdr2D ? 2 : 1; return GetNumPixels( viewport ) * numChannels * encoding; } public static RenderingDevice.DataFormat GetDataFormat( Viewport viewport ) { var imageFormat = viewport.GetTexture().GetImage().GetFormat(); return ImageFormatToDataFormat( imageFormat ); } public static RDTextureFormat FormatCopy( RDTextureFormat tf ) { var format = new RDTextureFormat(); format.Format = tf.Format; format.Width = tf.Width; format.Height = tf.Height; format.Depth = tf.Depth; format.ArrayLayers = tf.ArrayLayers; format.Mipmaps = tf.Mipmaps; format.TextureType = tf.TextureType; format.Samples = tf.Samples; format.UsageBits = tf.UsageBits; format.IsResolveBuffer = tf.IsResolveBuffer; format.IsDiscardable = tf.IsDiscardable; return tf; } public static RenderingDevice.DataFormat DataFormat( int channels, PixelChannelEncoding encoding ) { if ( 1 == channels ) { return DataFormat_1Channel( encoding ); } if ( 2 == channels ) { return DataFormat_2Channels( encoding ); } if ( 3 == channels ) { return DataFormat_3Channels( encoding ); } if ( 4 == channels ) { return DataFormat_4Channels( encoding ); } return RenderingDevice.DataFormat.Max; } public static RenderingDevice.DataFormat ImageFormatToDataFormat( Image.Format imageFormat ) { RJLog.Log( "ImageFormatToDataFormat", imageFormat ); switch ( imageFormat ) { case Image.Format.Rgb8: return RenderingDevice.DataFormat.R8G8B8Uint; case Image.Format.Rgba8: return RenderingDevice.DataFormat.R8G8B8A8Uint; case Image.Format.Rgbah: return RenderingDevice.DataFormat.R16G16B16A16Sfloat; } return RenderingDevice.DataFormat.Max; } public static Image.Format DataFormatToImageFormat( RenderingDevice.DataFormat dataFormat ) { switch ( dataFormat ) { case RenderingDevice.DataFormat.R8G8B8A8Uint: return Image.Format.Rgba8; case RenderingDevice.DataFormat.R8G8B8Uint: return Image.Format.Rgb8; case RenderingDevice.DataFormat.R16G16B16A16Sfloat: return Image.Format.Rgbah; } return Image.Format.Max; } public static RenderingDevice.DataFormat DataFormat_1Channel( PixelChannelEncoding encoding ) { switch ( encoding ) { case PixelChannelEncoding.uInt8: return RenderingDevice.DataFormat.R8Uint; } return RenderingDevice.DataFormat.Max; } public static RenderingDevice.DataFormat DataFormat_2Channels( PixelChannelEncoding encoding ) { switch ( encoding ) { case PixelChannelEncoding.uInt8: return RenderingDevice.DataFormat.R8Uint; } return RenderingDevice.DataFormat.Max; } public static RenderingDevice.DataFormat DataFormat_3Channels( PixelChannelEncoding encoding ) { switch ( encoding ) { case PixelChannelEncoding.uInt8: return RenderingDevice.DataFormat.R8G8B8Uint; case PixelChannelEncoding.sInt8: return RenderingDevice.DataFormat.R8G8B8Sint; case PixelChannelEncoding.uInt16: return RenderingDevice.DataFormat.R16G16B16Uint; case PixelChannelEncoding.sInt16: return RenderingDevice.DataFormat.R16G16B16Sint; case PixelChannelEncoding.sFloat16: return RenderingDevice.DataFormat.R16G16B16Sfloat; case PixelChannelEncoding.uNorm16: return RenderingDevice.DataFormat.R16G16B16Unorm; case PixelChannelEncoding.sNorm16: return RenderingDevice.DataFormat.R16G16B16Snorm; case PixelChannelEncoding.uInt32: return RenderingDevice.DataFormat.R32G32B32Uint; case PixelChannelEncoding.sInt32: return RenderingDevice.DataFormat.R32G32B32Sint; case PixelChannelEncoding.sFloat32: return RenderingDevice.DataFormat.R32G32B32Sfloat; } return RenderingDevice.DataFormat.Max; } public static RenderingDevice.DataFormat DataFormat_4Channels( PixelChannelEncoding encoding ) { switch ( encoding ) { case PixelChannelEncoding.uInt8: return RenderingDevice.DataFormat.R8G8B8A8Uint; case PixelChannelEncoding.sInt8: return RenderingDevice.DataFormat.R8G8B8A8Sint; case PixelChannelEncoding.uInt16: return RenderingDevice.DataFormat.R16G16B16A16Uint; case PixelChannelEncoding.sInt16: return RenderingDevice.DataFormat.R16G16B16A16Sint; case PixelChannelEncoding.sFloat16: return RenderingDevice.DataFormat.R16G16B16A16Sfloat; case PixelChannelEncoding.uNorm16: return RenderingDevice.DataFormat.R16G16B16A16Unorm; case PixelChannelEncoding.sNorm16: return RenderingDevice.DataFormat.R16G16B16A16Snorm; case PixelChannelEncoding.uInt32: return RenderingDevice.DataFormat.R32G32B32A32Uint; case PixelChannelEncoding.sInt32: return RenderingDevice.DataFormat.R32G32B32A32Sint; case PixelChannelEncoding.sFloat32: return RenderingDevice.DataFormat.R32G32B32A32Sfloat; } return RenderingDevice.DataFormat.Max; } // m: case ((?:\w|\.)*): return ((?:\w|\.)*); // r: case $2: return $1; public static PixelChannelEncoding GetPixelChannelEncoding( RenderingDevice.DataFormat format ) { switch ( format ) { case RenderingDevice.DataFormat.R8G8B8Sint: return PixelChannelEncoding.sInt8; case RenderingDevice.DataFormat.R16G16B16Uint: return PixelChannelEncoding.uInt16; case RenderingDevice.DataFormat.R16G16B16Sint: return PixelChannelEncoding.sInt16; case RenderingDevice.DataFormat.R16G16B16Sfloat: return PixelChannelEncoding.sFloat16; case RenderingDevice.DataFormat.R16G16B16Unorm: return PixelChannelEncoding.uNorm16; case RenderingDevice.DataFormat.R16G16B16Snorm: return PixelChannelEncoding.sNorm16; case RenderingDevice.DataFormat.R32G32B32Uint: return PixelChannelEncoding.uInt32; case RenderingDevice.DataFormat.R32G32B32Sint: return PixelChannelEncoding.sInt32; case RenderingDevice.DataFormat.R32G32B32Sfloat: return PixelChannelEncoding.sFloat32; case RenderingDevice.DataFormat.R8G8B8A8Uint: return PixelChannelEncoding.uInt8; case RenderingDevice.DataFormat.R8G8B8A8Sint: return PixelChannelEncoding.sInt8; case RenderingDevice.DataFormat.R16G16B16A16Uint: return PixelChannelEncoding.uInt16; case RenderingDevice.DataFormat.R16G16B16A16Sint: return PixelChannelEncoding.sInt16; case RenderingDevice.DataFormat.R16G16B16A16Sfloat: return PixelChannelEncoding.sFloat16; case RenderingDevice.DataFormat.R16G16B16A16Unorm: return PixelChannelEncoding.uNorm16; case RenderingDevice.DataFormat.R16G16B16A16Snorm: return PixelChannelEncoding.sNorm16; case RenderingDevice.DataFormat.R32G32B32A32Uint: return PixelChannelEncoding.uInt32; case RenderingDevice.DataFormat.R32G32B32A32Sint: return PixelChannelEncoding.sInt32; case RenderingDevice.DataFormat.R32G32B32A32Sfloat: return PixelChannelEncoding.sFloat32; } return PixelChannelEncoding.Unknown; } public static int GetNumChannels( RenderingDevice.DataFormat format ) { switch ( format ) { case RenderingDevice.DataFormat.R8G8B8Sint: case RenderingDevice.DataFormat.R16G16B16Uint: case RenderingDevice.DataFormat.R16G16B16Sint: case RenderingDevice.DataFormat.R16G16B16Sfloat: case RenderingDevice.DataFormat.R16G16B16Unorm: case RenderingDevice.DataFormat.R16G16B16Snorm: case RenderingDevice.DataFormat.R32G32B32Uint: case RenderingDevice.DataFormat.R32G32B32Sint: case RenderingDevice.DataFormat.R32G32B32Sfloat: return 3; case RenderingDevice.DataFormat.R8G8B8A8Uint: case RenderingDevice.DataFormat.R8G8B8A8Sint: case RenderingDevice.DataFormat.R16G16B16A16Uint: case RenderingDevice.DataFormat.R16G16B16A16Sint: case RenderingDevice.DataFormat.R16G16B16A16Sfloat: case RenderingDevice.DataFormat.R16G16B16A16Unorm: case RenderingDevice.DataFormat.R16G16B16A16Snorm: case RenderingDevice.DataFormat.R32G32B32A32Uint: case RenderingDevice.DataFormat.R32G32B32A32Sint: case RenderingDevice.DataFormat.R32G32B32A32Sfloat: return 4; } return -1; } static void WriteUShort( ushort shortValue, byte[] output, int offset ) { output[ offset + 0 ] = (byte)(shortValue & 0xFF); output[ offset + 1 ] = (byte)((shortValue >> 8) & 0xFF); } static void WriteUNorm( float value, byte[] output, int offset ) { var shortValue = (ushort)( Mathf.Clamp( value, 0.0f, 1.0f ) * 65535 ); WriteUShort( shortValue, output, offset ); } static ushort FloatToHalf( float value ) { uint fbits = BitConverter.SingleToUInt32Bits(value); uint sign = (fbits >> 16) & 0x8000; uint val = (fbits & 0x7FFFFFFF); // NaN / Inf -> maximal darstellen if ( val > 0x47FFEFFF ) { return (ushort)(sign | 0x7FFF); } if ( val < 0x38800000) // Subnormal { uint mant = (val & 0x007FFFFF) | 0x00800000; int exp = 113 - (int)(val >> 23); mant = (mant >> exp); return (ushort)(sign | (mant >> 13)); } else { uint exp = (val >> 23) - 112; uint mant = (val & 0x007FFFFF); return (ushort)(sign | (exp << 10) | (mant >> 13)); } } static void WriteFloat16( float value, byte[] output, int offset ) { ushort r = FloatToHalf( value ); WriteUShort( r, output, offset ); } public static byte[] ColorToBytes( Color color, RenderingDevice.DataFormat format ) { switch ( format ) { case RenderingDevice.DataFormat.R8G8B8A8Uint: { var bytes = new byte[ 4 ]; bytes[ 0 ] = (byte) ( color.R * 255 ); bytes[ 1 ] = (byte) ( color.G * 255 ); bytes[ 2 ] = (byte) ( color.B * 255 ); bytes[ 3 ] = (byte) ( color.A * 255 ); return bytes; } case RenderingDevice.DataFormat.R16G16B16A16Unorm: { var bytes = new byte[ 8 ]; WriteUNorm( color.R, bytes, 0 ); WriteUNorm( color.G, bytes, 2 ); WriteUNorm( color.B, bytes, 4 ); WriteUNorm( color.A, bytes, 6 ); return bytes; } case RenderingDevice.DataFormat.R16G16B16A16Sfloat: { var bytes = new byte[ 8 ]; WriteFloat16( color.R, bytes, 0 ); WriteFloat16( color.G, bytes, 2 ); WriteFloat16( color.B, bytes, 4 ); WriteFloat16( color.A, bytes, 6 ); return bytes; } } return null; } } }