/// <summary> /// Creates an atlas with the given TexImage. /// </summary> /// <param name="textureList">The texture list.</param> /// <param name="forceSquaredAtlas">boolean to decide wheter the atlas will be squared (default is false).</param> /// <returns>An instance of <see cref="TexAtlas"/>.</returns> /// <exception cref="TextureToolsException">No available library could create the atlas.</exception> public TexAtlas CreateAtlas(List<TexImage> textureList, bool forceSquaredAtlas = false) { TexAtlas atlas = new TexAtlas(); AtlasCreationRequest request = new AtlasCreationRequest(textureList, forceSquaredAtlas); ITexLibrary library = FindLibrary(atlas, request); if(library == null) { Log.Error("No available library could create the atlas."); throw new TextureToolsException("No available library could create the atlas."); } atlas.Format = textureList[0].Format; foreach (TexImage texture in textureList) { if (texture.Format != atlas.Format) { Log.Error("The textures in the list must all habe the same format."); throw new TextureToolsException("The textures in the list must all habe the same format."); } texture.Update(); } ExecuteRequest(atlas, request); return atlas; }
/// <summary> /// Creates an atlas. /// </summary> /// <param name="atlas">The atlas.</param> /// <param name="request">The request.</param> /// <param name="atlasSizeIncrement">An indice used to increment the atlas size</param> public void Create(TexAtlas atlas, AtlasCreationRequest request, int atlasSizeIncrement) { Log.Info("Creating atlas ..."); // Initalizing Atlas : trying to determine the minimum atlas size and allocating the needed memory InitalizeAtlas(atlas, request, atlasSizeIncrement); // Ordering textures in decreasing order : best heuristic with this algorithm. if (atlasSizeIncrement == 0) OrderTexture(request); // Finding the best layout for the textures in the atlas Node tree = PositionTextures(atlas, request); // One of many textures couldn't be positioned which means the atlas is too small if (tree == null) { Marshal.FreeHGlobal(atlas.Data); Create(atlas, request, atlasSizeIncrement + 1); } else { // Everything went well, we can copy the textures data into the atlas CopyTexturesIntoAtlasMemory(tree, atlas); // Creating the atlas data CreateAtlasData(tree, atlas, request.TextureList.Count); } }
/// <summary> /// Orders the textures list in decreasing size. /// </summary> /// <param name="request">The request.</param> private void OrderTexture(AtlasCreationRequest request) { QuickSort(request.TextureList, 0, request.TextureList.Count - 1); }
/// <summary> /// Determines the positions of the textures in the Atlas. /// </summary> /// <param name="atlas">The atlas.</param> /// <param name="request">The request.</param> /// <returns>The binary tree containing the positioned textures or null if the atlas is too small.</returns> private Node PositionTextures(TexAtlas atlas, AtlasCreationRequest request) { Node root = new Node(0, 0, atlas.Width, atlas.Height); foreach (TexImage texture in request.TextureList) { if (!Insert(root, texture)) { return null; } } return root; }
/// <summary> /// Initalizes the atlas. Predictes the minimum size required by the atlas according to the textures /// </summary> /// <param name="atlas">The atlas.</param> /// <param name="request">The request.</param> private void InitalizeAtlas(TexAtlas atlas, AtlasCreationRequest request, int atlasSizeIncrement) { // Calculating the total number of pixels of every texture to be included int pixelCount = 0; bool hasMipMap = false; foreach (TexImage texture in request.TextureList) { pixelCount += texture.Width * texture.Height; if (texture.MipmapCount > 1) hasMipMap = true; } // setting the new size to the atlas int alpha = (int)Math.Log(pixelCount, 2) + 1 + atlasSizeIncrement; atlas.Width = (int)Math.Pow(2, alpha / 2); atlas.Height = (int)Math.Pow(2, alpha - alpha / 2); // If we want a square texture, we compute the max of the width and height and assign it to both if (request.ForceSquaredAtlas) { int size = Math.Max(atlas.Width, atlas.Height); atlas.Width = size; atlas.Height = size; } // Setting the TexImage features int rowPitch, slicePitch; Tools.ComputePitch(atlas.Format, atlas.Width, atlas.Height, out rowPitch, out slicePitch); atlas.RowPitch = rowPitch; atlas.SlicePitch = slicePitch; // Allocating memory if (hasMipMap) { int dataSize = 0; int mipmapCount = 0; int w, h; List<TexImage.SubImage> subImages = new List<TexImage.SubImage>(); w = atlas.Width; h = atlas.Height; while (w != 1 || h != 1) { Tools.ComputePitch(atlas.Format, w, h, out rowPitch, out slicePitch); subImages.Add(new TexImage.SubImage() { Data = IntPtr.Zero, DataSize = slicePitch, Width = w, Height = h, RowPitch = rowPitch, SlicePitch = slicePitch, }); dataSize += slicePitch; ++mipmapCount; w = w > 1 ? w >>= 1 : w; h = h > 1 ? h >>= 1 : h; } atlas.DataSize = dataSize; atlas.Data = Marshal.AllocHGlobal(atlas.DataSize); atlas.SubImageArray = subImages.ToArray(); int offset = 0; for (int i = 0; i < atlas.SubImageArray.Length; ++i) { atlas.SubImageArray[i].Data = new IntPtr(atlas.Data.ToInt64() + offset); offset += atlas.SubImageArray[i].DataSize; } atlas.MipmapCount = mipmapCount; } else { atlas.DataSize = atlas.SlicePitch; atlas.Data = Marshal.AllocHGlobal(atlas.DataSize); atlas.SubImageArray[0].Data = atlas.Data; atlas.SubImageArray[0].DataSize = atlas.DataSize; atlas.SubImageArray[0].Width = atlas.Width; atlas.SubImageArray[0].Height = atlas.Height; atlas.SubImageArray[0].RowPitch = rowPitch; atlas.SubImageArray[0].SlicePitch = slicePitch; } atlas.DisposingLibrary = this; }