/// <summary> /// /// </summary> /// <param name="textures"></param> void PackTextureAtlas(IEnumerable <VTTexture> textures, Allocator2D allocator) { foreach (var tex in textures) { var size = Math.Max(tex.Width / 128, tex.Height / 128); var addr = allocator.Alloc(size, tex.Name); tex.TexelOffsetX = addr.X * VTConfig.PageSize; tex.TexelOffsetY = addr.Y * VTConfig.PageSize; tex.TilesDirty = true; Log.Message("...add: {0} : {1}x{2} : tile[{3},{4}]", tex.Name, tex.Width, tex.Height, addr.X, addr.Y); } }
/// <summary> /// /// </summary> /// <param name="textures"></param> void RepackTextureAtlas(VTTextureTable vtexTable, Allocator2D allocator, DateTime targetWriteTime) { // // remove deleted and changes textures from allocator : // var blockInfo = allocator.GetAllocatedBlockInfo(); foreach (var block in blockInfo) { if (!vtexTable.Contains(block.Tag)) { Log.Message("...removed: {0}", block.Tag); allocator.Free(block.Address); } else { if (vtexTable[block.Tag].IsModified(targetWriteTime)) { Log.Message("...changed: {0}", block.Tag); allocator.Free(block.Address); } } } // // add missing textures (note, changed are already removed). // var blockDictionary = allocator.GetAllocatedBlockInfo().ToDictionary(bi => bi.Tag); var newTextureList = new List <VTTexture>(); foreach (var tex in vtexTable.SourceTextures) { Allocator2D.BlockInfo bi; if (blockDictionary.TryGetValue(tex.Name, out bi)) { tex.TexelOffsetX = bi.Address.X * VTConfig.PageSize; tex.TexelOffsetY = bi.Address.Y * VTConfig.PageSize; } else { newTextureList.Add(tex); } } PackTextureAtlas(newTextureList, allocator); }
public static Allocator2D LoadState(BinaryReader reader) { // read header: var fourcc = reader.ReadFourCC(); var version = reader.ReadFourCC(); var size = reader.ReadInt32(); var allocator = new Allocator2D(size); // read nodes if depth-first order : var S = new Stack <Block>(); S.Push(allocator.rootBlock); while (S.Any()) { var block = S.Pop(); var fcc = reader.ReadFourCC(); block.Address.X = reader.ReadInt32(); block.Address.Y = reader.ReadInt32(); block.Size = reader.ReadInt32(); var state = (BlockState)reader.ReadInt32(); if (state == BlockState.Allocated) { block.Tag = reader.ReadString(); } if (state == BlockState.Split) { block.Split(); S.Push(block.BottomRight); S.Push(block.BottomLeft); S.Push(block.TopRight); S.Push(block.TopLeft); } } return(allocator); }
/// <summary> /// /// </summary> /// <param name="allocator"></param> /// <returns></returns> public static void SaveState(BinaryWriter writer, Allocator2D allocator) { // write header: writer.WriteFourCC("MLC2"); writer.WriteFourCC("1.00"); writer.Write(allocator.Size); // write nodes if depth-first order : var S = new Stack <Block>(); S.Push(allocator.rootBlock); while (S.Any()) { var block = S.Pop(); writer.WriteFourCC("BLCK"); writer.Write(block.Address.X); writer.Write(block.Address.Y); writer.Write(block.Size); writer.Write((int)block.State); if (block.State == BlockState.Allocated) { writer.Write(block.Tag); } if (block.State == BlockState.Split) { S.Push(block.BottomRight); S.Push(block.BottomLeft); S.Push(block.TopRight); S.Push(block.TopLeft); } } }
/// <summary> /// /// </summary> /// <param name="dir"></param> public static void RunTest(int size, int interations, string dir) { Log.Message("Allocator2D test: {0} {1} {2}", size, interations, dir); var dirInfo = Directory.CreateDirectory(dir); foreach (FileInfo file in dirInfo.GetFiles()) { file.Delete(); } var alloc = new Allocator2D(size); var image = new Image(size, size, Color.Black); var rand = new Random(); var list = new List <Int2>(); for (int i = 0; i < interations; i++) { Log.Message("{0,3:D3}/{1,3:D3}", i, interations); try { bool allocNotFree = rand.NextFloat(0, 1) < 0.5f; bool reloadState = rand.NextFloat(0, 1) < 0.1f; if (allocNotFree) { for (int j = 0; j < rand.Next(1, 16); j++) { //var sz = MathUtil.RoundUpNextPowerOf2( rand.Next(1,64) ); var sz = rand.Next(1, 64); var tag = string.Format("Block#{0,4:D4}##{1,4:D4}", i, sz); var a = alloc.Alloc(sz, tag); list.Add(a); DrawRectangle(image, a.X, a.Y, sz, sz, rand.NextColor(), false); } } else { for (int j = 0; j < rand.Next(1, 16); j++) { if (!list.Any()) { break; } var id = rand.Next(list.Count); var fa = list[id]; list.RemoveAt(id); var sz = alloc.Free(fa); DrawRectangle(image, fa.X, fa.Y, sz, sz, Color.Black, true); } } var imagePath = Path.Combine(dir, string.Format("allocTest{0,4:D4}.tga", i)); var stackPath = Path.Combine(dir, string.Format("allocTest{0,4:D4}.stk", i)); Image.SaveTga(image, imagePath); if (reloadState) { Log.Message("---- STATE RELOAD ----------------------------------"); using (var stream = File.OpenWrite(stackPath)) { Allocator2D.SaveState(stream, alloc); } using (var stream = File.OpenRead(stackPath)) { alloc = Allocator2D.LoadState(stream); } } } catch (Exception e) { Log.Error("Exception: {0}", e.Message); continue; } } Log.Message("Done!"); }
public static void SaveState(Stream targetStream, Allocator2D allocator) { var writer = new BinaryWriter(targetStream, Encoding.Default, true); SaveState(writer, allocator); }
/// <summary> /// /// </summary> /// <param name="buildContext"></param> public override void Process(AssetSource assetFile, BuildContext context) { Log.Message("-------- Virtual Texture --------"); var stopwatch = new Stopwatch(); stopwatch.Start(); var xmlFiles = Directory.EnumerateFiles(Path.Combine(Builder.FullInputDirectory, "vt"), "*.xml").ToList(); Log.Message("{0} megatexture segments", xmlFiles.Count); // // Process tiles : // using (var tileStorage = context.GetVTStorage()) { var pageTable = CreateVTTextureTable(xmlFiles, context, tileStorage); // // Get allocator and pack/repack textures : // Allocator2D allocator = null; if (tileStorage.FileExists(targetAllocator) && tileStorage.FileExists(targetMegatexture)) { Log.Message("Loading VT allocator..."); using (var allocStream = tileStorage.OpenRead(targetAllocator)) { allocator = Allocator2D.LoadState(allocStream); var targetTime = tileStorage.GetLastWriteTimeUtc(targetMegatexture); Log.Message("Repacking textures to atlas..."); RepackTextureAtlas(pageTable, allocator, targetTime); } } else { allocator = new Allocator2D(VTConfig.VirtualPageCount); Log.Message("Packing ALL textures to atlas..."); PackTextureAtlas(pageTable.SourceTextures, allocator); } Log.Message("Saving VT allocator..."); using (var allocStream = tileStorage.OpenWrite(targetAllocator)) { Allocator2D.SaveState(allocStream, allocator); } // // Generate top-level pages : // Log.Message("Generating pages..."); GenerateMostDetailedPages(pageTable.SourceTextures, context, pageTable, tileStorage); // // Generate mip-maps : // Log.Message("Generating mipmaps..."); for (int mip = 0; mip < VTConfig.MipCount - 1; mip++) { Log.Message("Generating mip level {0}/{1}...", mip, VTConfig.MipCount); GenerateMipLevels(context, pageTable, mip, tileStorage); } // // Write asset : // using (var stream = tileStorage.OpenWrite(targetMegatexture)) { using (var assetStream = AssetStream.OpenWrite(stream, "", new[] { "" })) { using (var sw = new BinaryWriter(assetStream)) { sw.Write(pageTable.SourceTextures.Count); foreach (var tex in pageTable.SourceTextures) { VTTexture.Write(tex, sw); } } } } } stopwatch.Stop(); Log.Message("{0}", stopwatch.Elapsed.ToString()); Log.Message("----------------"); }