// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT using System; using System.Drawing; using ClassicalSharp.GraphicsAPI; #if ANDROID using Android.Graphics; #endif namespace ClassicalSharp.Textures { /// Represents a 2D packed texture atlas that has been converted into an array of 1D atlases. public sealed class TerrainAtlas1D : IDisposable { internal int elementsPerAtlas1D; internal int elementsPerBitmap; public float invElementSize; public int[] TexIds; IGraphicsApi gfx; public TerrainAtlas1D( IGraphicsApi gfx ) { this.gfx = gfx; } public TextureRec GetTexRec( int texId, int uCount, out int index ) { index = texId / elementsPerAtlas1D; int y = texId % elementsPerAtlas1D; // Adjust coords to be slightly inside - fixes issues with AMD/ATI cards. return new TextureRec( 0, y * invElementSize, (uCount - 1) + 15.99f/16f, (15.99f/16f) * invElementSize ); } /// Returns the index of the 1D texture within the array of 1D textures /// containing the given texture id. public int Get1DIndex( int texId ) { return texId / elementsPerAtlas1D; } /// Returns the index of the given texture id within a 1D texture. public int Get1DRowId( int texId ) { return texId % elementsPerAtlas1D; } public void UpdateState( TerrainAtlas2D atlas2D ) { int maxVerticalSize = Math.Min( 4096, gfx.MaxTextureDimensions ); int elementsPerFullAtlas = maxVerticalSize / atlas2D.elementSize; int totalElements = TerrainAtlas2D.RowsCount * TerrainAtlas2D.ElementsPerRow; int atlasesCount = Utils.CeilDiv( totalElements, elementsPerFullAtlas ); elementsPerAtlas1D = Math.Min( elementsPerFullAtlas, totalElements ); int atlas1DHeight = Utils.NextPowerOf2( elementsPerAtlas1D * atlas2D.elementSize ); Convert2DTo1D( atlas2D, atlasesCount, atlas1DHeight ); elementsPerBitmap = atlas1DHeight / atlas2D.elementSize; invElementSize = 1f / elementsPerBitmap; } void Convert2DTo1D( TerrainAtlas2D atlas2D, int atlasesCount, int atlas1DHeight ) { TexIds = new int[atlasesCount]; Utils.LogDebug( "Loaded new atlas: {0} bmps, {1} per bmp", atlasesCount, elementsPerAtlas1D ); int index = 0; using( FastBitmap atlas = new FastBitmap( atlas2D.AtlasBitmap, true, true ) ) { for( int i = 0; i < TexIds.Length; i++ ) Make1DTexture( i, atlas, atlas2D, atlas1DHeight, ref index ); } } void Make1DTexture( int i, FastBitmap atlas, TerrainAtlas2D atlas2D, int atlas1DHeight, ref int index ) { int elemSize = atlas2D.elementSize; using( Bitmap atlas1d = Platform.CreateBmp( atlas2D.elementSize, atlas1DHeight ) ) using( FastBitmap dst = new FastBitmap( atlas1d, true, false ) ) { for( int index1D = 0; index1D < elementsPerAtlas1D; index1D++ ) { FastBitmap.MovePortion( (index & 0x0F) * elemSize, (index >> 4) * elemSize, 0, index1D * elemSize, atlas, dst, elemSize ); index++; } TexIds[i] = gfx.CreateTexture( dst, true ); } } public int CalcMaxUsedRow( TerrainAtlas2D atlas2D, BlockInfo info ) { int maxVerSize = Math.Min( 4096, gfx.MaxTextureDimensions ); int verElements = maxVerSize / atlas2D.elementSize; int totalElements = GetMaxUsedRow( info.textures ) * TerrainAtlas2D.ElementsPerRow; return Utils.CeilDiv( totalElements, verElements ); } int GetMaxUsedRow( byte[] textures ) { int maxElem = 0; for( int i = 0; i < textures.Length; i++ ) maxElem = Math.Max( maxElem, textures[i] ); return (maxElem >> 4) + 1; } public void Dispose() { if( TexIds == null ) return; for( int i = 0; i < TexIds.Length; i++ ) { gfx.DeleteTexture( ref TexIds[i] ); } } } }