Exemple #1
0
        private void CopyAllocatedItemsToAtlas(List <TextureTools.AtlasItem> items, string atlasChain, int atlasId, Size size)
        {
            var atlasPath   = AssetCooker.GetAtlasPath(atlasChain, atlasId);
            var atlasPixels = new Color4[size.Width * size.Height];

            foreach (var item in items.Where(i => i.Allocated))
            {
                var atlasRect = item.AtlasRect;
                using (var bitmap = TextureTools.OpenAtlasItemBitmapAndRescaleIfNeeded(AssetCooker.Platform, item)) {
                    CopyPixels(bitmap, atlasPixels, atlasRect.A.X, atlasRect.A.Y, size.Width, size.Height);
                }
                var atlasPart = new TextureAtlasElement.Params {
                    AtlasRect = atlasRect,
                    AtlasPath = Path.ChangeExtension(atlasPath, null)
                };
                var srcPath = Path.ChangeExtension(item.Path, item.SourceExtension);
                InternalPersistence.Instance.WriteObjectToBundle(AssetCooker.OutputBundle, item.Path, atlasPart, Persistence.Format.Binary,
                                                                 item.SourceExtension, AssetCooker.InputBundle.GetFileLastWriteTime(srcPath), AssetAttributes.None, item.CookingRules.SHA1);
                // Delete non-atlased texture since now its useless
                var texturePath = Path.ChangeExtension(item.Path, AssetCooker.GetPlatformTextureExtension());
                if (AssetCooker.OutputBundle.FileExists(texturePath))
                {
                    AssetCooker.DeleteFileFromBundle(texturePath);
                }
                UserInterface.Instance.IncreaseProgressBar();
            }
            Console.WriteLine("+ " + atlasPath);
            var firstItem = items.First(i => i.Allocated);

            using (var atlas = new Bitmap(atlasPixels, size.Width, size.Height)) {
                AssetCooker.ImportTexture(atlasPath, atlas, firstItem.CookingRules, AssetCooker.InputBundle.GetFileLastWriteTime(atlasPath), CookingRulesSHA1: null);
            }
        }
Exemple #2
0
        private bool Converter(string srcPath, string dstPath)
        {
            var rules = AssetCooker.CookingRulesMap[Path.ChangeExtension(dstPath, originalTextureExtension)];

            if (rules.TextureAtlas != null)
            {
                // No need to cache this texture since it is a part of texture atlas.
                return(false);
            }
            using (var stream = File.OpenRead(srcPath)) {
                var bitmap = new Bitmap(stream);
                if (TextureTools.ShouldDownscale(AssetCooker.Platform, bitmap, rules))
                {
                    var scaledBitmap = TextureTools.DownscaleTexture(AssetCooker.Platform, bitmap, srcPath, rules);
                    bitmap.Dispose();
                    bitmap = scaledBitmap;
                }
                AssetCooker.ImportTexture(dstPath, bitmap, rules, File.GetLastWriteTime(srcPath), rules.SHA1);
                bitmap.Dispose();
            }
            return(true);
        }
Exemple #3
0
        private IEnumerable <Size> EnumerateAtlasSizes(bool squareAtlas, int minSize)
        {
            if (squareAtlas)
            {
                for (var i = minSize; i <= TextureTools.GetMaxAtlasSize().Width; i *= 2)
                {
                    yield return(new Size(i, i));
                }
            }
            else
            {
                for (var i = minSize; i <= TextureTools.GetMaxAtlasSize().Width / 2; i *= 2)
                {
                    yield return(new Size(i, i));

                    yield return(new Size(i * 2, i));

                    yield return(new Size(i, i * 2));
                }
                yield return(TextureTools.GetMaxAtlasSize());
            }
        }
Exemple #4
0
        public void ImportTexture(string path, Bitmap texture, ICookingRules rules, DateTime time, byte[] CookingRulesSHA1)
        {
            var textureParamsPath = Path.ChangeExtension(path, ".texture");
            var textureParams     = new TextureParams {
                WrapMode  = rules.WrapMode,
                MinFilter = rules.MinFilter,
                MagFilter = rules.MagFilter,
            };

            if (!AreTextureParamsDefault(rules))
            {
                TextureTools.UpscaleTextureIfNeeded(ref texture, rules, false);
                var isNeedToRewriteTexParams = true;
                if (AssetBundle.FileExists(textureParamsPath))
                {
                    var oldTexParams = InternalPersistence.Instance.ReadObject <TextureParams>(textureParamsPath, AssetBundle.OpenFile(textureParamsPath));
                    isNeedToRewriteTexParams = !oldTexParams.Equals(textureParams);
                }
                if (isNeedToRewriteTexParams)
                {
                    InternalPersistence.Instance.WriteObjectToBundle(AssetBundle, textureParamsPath, textureParams, Persistence.Format.Binary, ".texture",
                                                                     File.GetLastWriteTime(textureParamsPath), AssetAttributes.None, null);
                }
            }
            else
            {
                if (AssetBundle.FileExists(textureParamsPath))
                {
                    DeleteFileFromBundle(textureParamsPath);
                }
            }
            if (rules.GenerateOpacityMask)
            {
                var maskPath = Path.ChangeExtension(path, ".mask");
                OpacityMaskCreator.CreateMask(AssetBundle, texture, maskPath);
            }
            var attributes = AssetAttributes.ZippedDeflate;

            if (!TextureConverterUtils.IsPowerOf2(texture.Width) || !TextureConverterUtils.IsPowerOf2(texture.Height))
            {
                attributes |= AssetAttributes.NonPowerOf2Texture;
            }
            switch (Target.Platform)
            {
            case TargetPlatform.Android:
                //case TargetPlatform.iOS:
                var f = rules.PVRFormat;
                if (f == PVRFormat.ARGB8 || f == PVRFormat.RGB565 || f == PVRFormat.RGBA4)
                {
                    TextureConverter.RunPVRTexTool(texture, AssetBundle, path, attributes, rules.MipMaps, rules.HighQualityCompression, rules.PVRFormat, CookingRulesSHA1, time);
                }
                else
                {
                    TextureConverter.RunEtcTool(texture, AssetBundle, path, attributes, rules.MipMaps, rules.HighQualityCompression, CookingRulesSHA1, time);
                }
                break;

            case TargetPlatform.iOS:
                TextureConverter.RunPVRTexTool(texture, AssetBundle, path, attributes, rules.MipMaps, rules.HighQualityCompression, rules.PVRFormat, CookingRulesSHA1, time);
                break;

            case TargetPlatform.Win:
            case TargetPlatform.Mac:
                TextureConverter.RunNVCompress(texture, AssetBundle, path, attributes, rules.DDSFormat, rules.MipMaps, CookingRulesSHA1, time);
                break;

            default:
                throw new Lime.Exception();
            }
        }
Exemple #5
0
        private int PackItemsToAtlas(string atlasChain, List <TextureTools.AtlasItem> items,
                                     AtlasOptimization atlasOptimization, int initialAtlasId, bool squareAtlas)
        {
            // Sort images in descending size order
            items.Sort((x, y) => {
                var a = Math.Max(x.BitmapInfo.Width, x.BitmapInfo.Height);
                var b = Math.Max(y.BitmapInfo.Width, y.BitmapInfo.Height);
                return(b - a);
            });

            var atlasId = initialAtlasId;

            while (items.Count > 0)
            {
                if (atlasId >= AssetCooker.MaxAtlasChainLength)
                {
                    throw new Lime.Exception("Too many textures in the atlas chain {0}", atlasChain);
                }
                var    bestSize     = new Size(0, 0);
                double bestPackRate = 0;
                int    minItemsLeft = Int32.MaxValue;

                // TODO: Fix for non-square atlases
                var maxTextureSize = items.Max(item => Math.Max(item.BitmapInfo.Height, item.BitmapInfo.Width));
                var minAtlasSize   = Math.Max(64, TextureTools.CalcUpperPowerOfTwo(maxTextureSize));

                foreach (var size in EnumerateAtlasSizes(squareAtlas: squareAtlas, minSize: minAtlasSize))
                {
                    double packRate;
                    var    prevAllocated = items.Where(i => i.Allocated).ToList();
                    PackItemsToAtlas(items, size, out packRate);
                    switch (atlasOptimization)
                    {
                    case AtlasOptimization.Memory:
                        if (packRate * 0.95f > bestPackRate)
                        {
                            bestPackRate = packRate;
                            bestSize     = size;
                        }
                        break;

                    case AtlasOptimization.DrawCalls: {
                        var notAllocatedCount = items.Count(item => !item.Allocated);
                        if (notAllocatedCount < minItemsLeft)
                        {
                            minItemsLeft = notAllocatedCount;
                            bestSize     = size;
                        }
                        else if (notAllocatedCount == minItemsLeft)
                        {
                            if (items.Where(i => i.Allocated).SequenceEqual(prevAllocated))
                            {
                                continue;
                            }
                            else
                            {
                                minItemsLeft = notAllocatedCount;
                                bestSize     = size;
                            }
                        }
                        if (notAllocatedCount == 0)
                        {
                            goto end;
                        }
                        break;
                    }
                    }
                }
end:
                if (atlasOptimization == AtlasOptimization.Memory && bestPackRate == 0)
                {
                    throw new Lime.Exception("Failed to create atlas '{0}'", atlasChain);
                }
                PackItemsToAtlas(items, bestSize, out bestPackRate);
                CopyAllocatedItemsToAtlas(items, atlasChain, atlasId, bestSize);
                items.RemoveAll(x => x.Allocated);
                atlasId++;
            }
            return(atlasId);
        }
Exemple #6
0
        private void BuildAtlasChain(string atlasChain)
        {
            for (var i = 0; i < AssetCooker.MaxAtlasChainLength; i++)
            {
                var atlasPath = AssetCooker.GetAtlasPath(atlasChain, i);
                if (AssetCooker.OutputBundle.FileExists(atlasPath))
                {
                    AssetCooker.DeleteFileFromBundle(atlasPath);
                }
                else
                {
                    break;
                }
            }
            var pluginItems = new Dictionary <string, List <TextureTools.AtlasItem> >();
            var items       = new Dictionary <AtlasOptimization, List <TextureTools.AtlasItem> >();

            items[AtlasOptimization.Memory]    = new List <TextureTools.AtlasItem>();
            items[AtlasOptimization.DrawCalls] = new List <TextureTools.AtlasItem>();
            foreach (var fileInfo in AssetBundle.Current.EnumerateFileInfos(null, textureExtension))
            {
                var cookingRules = AssetCooker.CookingRulesMap[fileInfo.Path];
                if (cookingRules.TextureAtlas == atlasChain)
                {
                    var item = new TextureTools.AtlasItem {
                        Path            = Path.ChangeExtension(fileInfo.Path, atlasPartExtension),
                        CookingRules    = cookingRules,
                        SourceExtension = Path.GetExtension(fileInfo.Path)
                    };
                    var bitmapInfo = TextureTools.BitmapInfo.FromFile(AssetCooker.InputBundle, fileInfo.Path);
                    if (bitmapInfo == null)
                    {
                        using (var bitmap = TextureTools.OpenAtlasItemBitmapAndRescaleIfNeeded(AssetCooker.Platform, item)) {
                            item.BitmapInfo = TextureTools.BitmapInfo.FromBitmap(bitmap);
                        }
                    }
                    else
                    {
                        var srcTexturePath = AssetPath.Combine(The.Workspace.AssetsDirectory, Path.ChangeExtension(item.Path, item.SourceExtension));
                        if (TextureTools.ShouldDownscale(AssetCooker.Platform, bitmapInfo, item.CookingRules))
                        {
                            TextureTools.DownscaleTextureInfo(AssetCooker.Platform, bitmapInfo, srcTexturePath, item.CookingRules);
                        }
                        // Ensure that no image exceeded maxAtlasSize limit
                        TextureTools.DownscaleTextureToFitAtlas(bitmapInfo, srcTexturePath);
                        item.BitmapInfo = bitmapInfo;
                    }
                    var k = cookingRules.AtlasPacker;
                    if (!string.IsNullOrEmpty(k) && k != "Default")
                    {
                        List <TextureTools.AtlasItem> l;
                        if (!pluginItems.TryGetValue(k, out l))
                        {
                            pluginItems.Add(k, l = new List <TextureTools.AtlasItem>());
                        }
                        l.Add(item);
                    }
                    else
                    {
                        items[cookingRules.AtlasOptimization].Add(item);
                    }
                }
            }
            var initialAtlasId = 0;

            foreach (var kv in items)
            {
                if (kv.Value.Any())
                {
                    if (AssetCooker.Platform == TargetPlatform.iOS)
                    {
                        Predicate <PVRFormat> isRequireSquare = (format) => {
                            return
                                (format == PVRFormat.PVRTC2 ||
                                 format == PVRFormat.PVRTC4 ||
                                 format == PVRFormat.PVRTC4_Forced);
                        };
                        var square    = kv.Value.Where(item => isRequireSquare(item.CookingRules.PVRFormat)).ToList();
                        var nonSquare = kv.Value.Where(item => !isRequireSquare(item.CookingRules.PVRFormat)).ToList();
                        initialAtlasId = PackItemsToAtlas(atlasChain, square, kv.Key, initialAtlasId, true);
                        initialAtlasId = PackItemsToAtlas(atlasChain, nonSquare, kv.Key, initialAtlasId, false);
                    }
                    else
                    {
                        initialAtlasId = PackItemsToAtlas(atlasChain, kv.Value, kv.Key, initialAtlasId, false);
                    }
                }
            }
            var packers = PluginLoader.CurrentPlugin.AtlasPackers.ToDictionary(i => i.Metadata.Id, i => i.Value);

            foreach (var kv in pluginItems)
            {
                if (!packers.ContainsKey(kv.Key))
                {
                    throw new InvalidOperationException($"Packer {kv.Key} not found");
                }
                initialAtlasId = packers[kv.Key](atlasChain, kv.Value, initialAtlasId);
            }
        }