< Summary

Class:DCL.ABConverter.Core
Assembly:ABConverter
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/ABConverter/Core.cs
Covered lines:0
Uncovered lines:322
Coverable lines:322
Total lines:825
Line coverage:0% (0 of 322)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Core(...)0%30500%
Core()0%2100%
Convert(...)0%2100%
ConvertDumpedAssets(...)0%6200%
ProcessSkippedAssets(...)0%12300%
GetAssetDependenciesMappingPairs(...)0%90900%
DumpAssets(...)0%12300%
FilterDumpList(...)0%12300%
DumpGltf(...)0%72800%
DumpRawAssets(...)0%42600%
DumpImportableAssets(...)0%30500%
ReduceTextureSizeIfNeeded(...)0%30500%
DownloadAsset(...)0%20400%
RetrieveAndInjectTexture(...)0%12300%
RetrieveAndInjectBuffer(...)0%6200%
MarkAllAssetBundles(...)0%6200%
BuildAssetBundles(...)0%20400%
CleanAndExit(...)0%6200%
SetDeterministicAssetDatabaseGuid(...)0%2100%
CleanAssetBundleFolder(...)0%2100%
PopulateLowercaseMappings(...)0%12300%
MarkShaderAssetBundle()0%2100%
InitializeDirectoryPaths(...)0%2100%
CleanupWorkingFolders()0%6200%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/ABConverter/Core.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.IO;
 4using System.Linq;
 5using System.Net.Http;
 6using System.Text.RegularExpressions;
 7using Unity.EditorCoroutines.Editor;
 8using UnityEditor;
 9using UnityEngine;
 10using UnityEngine.Networking;
 11using UnityGLTF;
 12using UnityGLTF.Cache;
 13using GLTF;
 14using GLTF.Schema;
 15
 16namespace DCL.ABConverter
 17{
 18    public class Core
 19    {
 20        public enum ErrorCodes
 21        {
 22            SUCCESS,
 23            UNDEFINED,
 24            SCENE_LIST_NULL,
 25            ASSET_BUNDLE_BUILD_FAIL,
 26            SOME_ASSET_BUNDLES_SKIPPED
 27        }
 28
 29        public class State
 30        {
 31            public enum Step
 32            {
 33                IDLE,
 34                DUMPING_ASSETS,
 35                BUILDING_ASSET_BUNDLES,
 36                FINISHED,
 37            }
 38
 039            public Step step { get; internal set; }
 040            public ErrorCodes lastErrorCode { get; internal set; }
 41        }
 42
 043        public readonly State state = new State();
 44
 45        private const string MAIN_SHADER_AB_NAME = "MainShader_Delete_Me";
 46        private const float MAX_TEXTURE_SIZE = 512f;
 47
 48        internal readonly string finalDownloadedPath;
 49        internal readonly string finalDownloadedAssetDbPath;
 050        public Dictionary<string, string> hashLowercaseToHashProper = new Dictionary<string, string>();
 051        internal bool generateAssetBundles = true;
 52
 53        public Client.Settings settings;
 54
 55        private float startTime;
 56        private int totalAssets;
 57        private int skippedAssets;
 58
 59        private Environment env;
 060        private static Logger log = new Logger("ABConverter.Core");
 61        private string logBuffer;
 62
 063        public Core(Environment env, Client.Settings settings = null)
 64        {
 065            this.env = env;
 66
 067            this.settings = settings?.Clone() ?? new Client.Settings();
 68
 069            finalDownloadedPath = PathUtils.FixDirectorySeparator(Config.DOWNLOADED_PATH_ROOT + Config.DASH);
 070            finalDownloadedAssetDbPath = PathUtils.FixDirectorySeparator(Config.ASSET_BUNDLES_PATH_ROOT + Config.DASH);
 71
 072            if (Utils.ParseOption(Config.CLI_SET_CUSTOM_OUTPUT_ROOT_PATH, 1, out string[] outputPath))
 73            {
 074                finalDownloadedAssetDbPath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), outputPath[0] + "/"
 75
 076                Debug.Log($"ABConverter Core: -output PATH param found, setting final ABPath as '{finalDownloadedAssetDb
 077            }
 78            else
 79            {
 080                Debug.Log($"ABConverter Core: -output PATH param NOT found, setting final ABPath as '{finalDownloadedAss
 81            }
 82
 083            settings.finalAssetBundlePath = finalDownloadedAssetDbPath;
 84
 085            log.verboseEnabled = this.settings.verbose;
 86
 087            state.step = State.Step.IDLE;
 088        }
 89
 90        /// <summary>
 91        /// Generate asset bundles using a MappingPair list.
 92        ///
 93        /// This method will try to dump GLTF models, textures and buffers from the given mapping pair list,
 94        /// tag them for asset bundle building and finally build them.
 95        ///
 96        /// If the GLTF have external references of any texture/buffer of the same list, the references will be
 97        /// resolved correctly on the GLTF importer and then correctly converted to Asset Bundles references.
 98        ///
 99        /// Shader assets will be stripped from the generated bundles.
 100        /// </summary>
 101        /// <param name="rawContents">A list detailing assets to be dumped</param>
 102        /// <param name="OnFinish">End callback with the proper ErrorCode</param>
 103        public void Convert(ContentServerUtils.MappingPair[] rawContents, Action<ErrorCodes> OnFinish = null)
 104        {
 0105            OnFinish -= CleanAndExit;
 0106            OnFinish += CleanAndExit;
 107
 0108            startTime = Time.realtimeSinceStartup;
 109
 0110            log.Info($"Conversion start... free space in disk: {PathUtils.GetFreeSpace()}");
 111
 0112            InitializeDirectoryPaths(settings.clearDirectoriesOnStart);
 0113            PopulateLowercaseMappings(rawContents);
 114
 0115            float timer = Time.realtimeSinceStartup;
 0116            bool shouldGenerateAssetBundles = generateAssetBundles;
 0117            bool assetsAlreadyDumped = false;
 118
 119            //TODO(Brian): Use async-await instead of Application.update
 120            void UpdateLoop()
 121            {
 122                try
 123                {
 124                    //NOTE(Brian): We have to check this because the ImportAsset for GLTFs is not synchronous, and must 
 125                    //             after the import asset finished. Therefore, we have to make sure those calls finished
 0126                    if (!GLTFImporter.finishedImporting && Time.realtimeSinceStartup - timer < 60)
 0127                        return;
 128
 0129                    env.assetDatabase.Refresh();
 130
 0131                    if (!assetsAlreadyDumped)
 132                    {
 0133                        state.step = State.Step.DUMPING_ASSETS;
 0134                        shouldGenerateAssetBundles |= DumpAssets(rawContents);
 0135                        assetsAlreadyDumped = true;
 0136                        timer = Time.realtimeSinceStartup;
 137
 0138                        if (settings.dumpOnly)
 0139                            shouldGenerateAssetBundles = false;
 140
 141                        //NOTE(Brian): return in order to wait for GLTFImporter.finishedImporting flag, as it will set a
 0142                        return;
 143                    }
 144
 0145                    EditorApplication.update -= UpdateLoop;
 146
 0147                    if (shouldGenerateAssetBundles && generateAssetBundles)
 148                    {
 149                        AssetBundleManifest manifest;
 150
 0151                        state.step = State.Step.BUILDING_ASSET_BUNDLES;
 152
 0153                        if (BuildAssetBundles(out manifest))
 154                        {
 0155                            CleanAssetBundleFolder(manifest.GetAllAssetBundles());
 156
 0157                            state.lastErrorCode = ErrorCodes.SUCCESS;
 0158                            state.step = State.Step.FINISHED;
 0159                        }
 160                        else
 161                        {
 0162                            state.lastErrorCode = ErrorCodes.ASSET_BUNDLE_BUILD_FAIL;
 0163                            state.step = State.Step.FINISHED;
 164                        }
 0165                    }
 166                    else
 167                    {
 0168                        state.lastErrorCode = ErrorCodes.SUCCESS;
 0169                        state.step = State.Step.FINISHED;
 170                    }
 0171                }
 0172                catch (Exception e)
 173                {
 0174                    log.Error(e.Message + "\n" + e.StackTrace);
 0175                    state.lastErrorCode = ErrorCodes.UNDEFINED;
 0176                    state.step = State.Step.FINISHED;
 0177                    EditorApplication.update -= UpdateLoop;
 0178                }
 179
 0180                if (generateAssetBundles)
 181                {
 0182                    EditorCoroutineUtility.StartCoroutineOwnerless(VisualTests.TestConvertedAssets(
 183                        env: env,
 184                        OnFinish: (skippedAssetsCount) =>
 185                        {
 0186                            ProcessSkippedAssets(skippedAssetsCount);
 187
 0188                            OnFinish?.Invoke(state.lastErrorCode);
 0189                        }));
 0190                }
 191                else
 192                {
 0193                    OnFinish?.Invoke(state.lastErrorCode);
 194                }
 0195            }
 196
 0197            EditorApplication.update += UpdateLoop;
 0198        }
 199
 200        public void ConvertDumpedAssets(Action<ErrorCodes> OnFinish = null)
 201        {
 0202            if (!BuildAssetBundles(out AssetBundleManifest manifest))
 0203                return;
 204
 0205            CleanAssetBundleFolder(manifest.GetAllAssetBundles());
 206
 0207            EditorCoroutineUtility.StartCoroutineOwnerless(VisualTests.TestConvertedAssets(
 208                env: env,
 209                OnFinish: (skippedAssetsCount) =>
 210                {
 0211                    ProcessSkippedAssets(skippedAssetsCount);
 212
 0213                    OnFinish?.Invoke(state.lastErrorCode);
 0214                }));
 0215        }
 216
 217        private void ProcessSkippedAssets(int skippedAssetsCount)
 218        {
 0219            if (skippedAssetsCount <= 0)
 0220                return;
 221
 0222            skippedAssets = skippedAssetsCount;
 223
 0224            if (skippedAssets >= totalAssets)
 0225                state.lastErrorCode = ErrorCodes.ASSET_BUNDLE_BUILD_FAIL;
 226            else
 0227                state.lastErrorCode = ErrorCodes.SOME_ASSET_BUNDLES_SKIPPED;
 0228        }
 229
 230        /// <summary>
 231        /// Parses a GLTF and populates a List<ContentServerUtils.MappingPair> with its dependencies
 232        /// </summary>
 233        /// <param name="assetHash">The asset's content server hash</param>
 234        /// <param name="assetFilename">The asset's content server file name</param>
 235        /// <param name="sceneCid">The asset scene ID</param>
 236        /// <param name="mappingPairsList">The reference of a list where the dependency mapping pairs will be added</par
 237        public void GetAssetDependenciesMappingPairs(string assetHash, string assetFilename, string sceneCid,  ref List<
 238        {
 239            // 1. Get all dependencies
 0240            List<AssetPath> gltfPaths = ABConverter.Utils.GetPathsFromPairs(finalDownloadedPath, new []
 241            {
 242                new ContentServerUtils.MappingPair
 243                {
 244                    file = assetFilename,
 245                    hash = assetHash
 246                }
 247            }, Config.gltfExtensions);
 248
 249            // Disable editor assets auto-import temporarily to avoid Unity trying to import the GLTF on its own, when w
 0250            AssetDatabase.StartAssetEditing();
 251
 0252            string path = DownloadAsset(gltfPaths[0]);
 253
 0254            if (string.IsNullOrEmpty(path))
 255            {
 0256                log.Error("Core - GetAssetDependenciesMappingPairs() - Invalid target asset data! aborting dependencies 
 0257                return;
 258            }
 259
 0260            log.Info($"Core - GetAssetDependenciesMappingPairs() -> path: {path}," +
 261                     $"\n file: {gltfPaths[0].file}," +
 262                     $"\n hash: {gltfPaths[0].hash}," +
 263                     $"\n pair: {gltfPaths[0].pair}, " +
 264                     $"\n basePath: {gltfPaths[0].basePath}," +
 265                     $"\n finalPath: {gltfPaths[0].finalPath}," +
 266                     $"\n finalMetaPath: {gltfPaths[0].finalMetaPath}");
 267
 268            // 2. Search for dependencies hashes in scene mappings and add them to the collection
 0269            using (var stream = File.OpenRead(path))
 270            {
 271                GLTFRoot gLTFRoot;
 0272                GLTFParser.ParseJson(stream, out gLTFRoot);
 273
 0274                ContentServerUtils.MappingsAPIData parcelInfoApiData = ABConverter.Utils.GetSceneMappingsData(env.webReq
 275
 0276                if (gLTFRoot.Buffers != null)
 277                {
 0278                    foreach (var asset in gLTFRoot.Buffers)
 279                    {
 0280                        if (string.IsNullOrEmpty(asset.Uri))
 281                            continue;
 282
 0283                        mappingPairsList.AddRange(parcelInfoApiData.data[0].content.contents.Where(x => x.file.Contains(
 284
 0285                        log.Info("Core - GetAssetDependenciesMappingPairs - Buffers -> Searching for... uri: " + asset.U
 286                    }
 287                }
 288
 0289                if (gLTFRoot.Images != null)
 290                {
 0291                    foreach (var asset in gLTFRoot.Images)
 292                    {
 0293                        if (string.IsNullOrEmpty(asset.Uri))
 294                            continue;
 0295                        mappingPairsList.AddRange(parcelInfoApiData.data[0].content.contents.Where(x => x.file.Contains(
 296
 0297                        log.Info("Core - GetAssetDependenciesMappingPairs - Images -> uri: " + asset.Uri + " -> name: " 
 298                    }
 299                }
 0300            }
 301
 302            // 3. Remove temporary GLTF file and re-enable editor assets auto-import
 0303            File.Delete(path);
 0304            AssetDatabase.StopAssetEditing();
 0305        }
 306
 307        /// <summary>
 308        /// Dump all assets and tag them for asset bundle building.
 309        /// </summary>
 310        /// <param name="rawContents">An array containing all the assets to be dumped.</param>
 311        /// <returns>true if succeeded</returns>
 312        private bool DumpAssets(ContentServerUtils.MappingPair[] rawContents)
 313        {
 0314            List<AssetPath> gltfPaths = ABConverter.Utils.GetPathsFromPairs(finalDownloadedPath, rawContents, Config.glt
 0315            List<AssetPath> bufferPaths = ABConverter.Utils.GetPathsFromPairs(finalDownloadedPath, rawContents, Config.b
 0316            List<AssetPath> texturePaths = ABConverter.Utils.GetPathsFromPairs(finalDownloadedPath, rawContents, Config.
 317
 0318            List<AssetPath> assetsToMark = new List<AssetPath>();
 319
 0320            if (!FilterDumpList(ref gltfPaths))
 0321                return false;
 322
 323            //NOTE(Brian): Prepare textures and buffers. We should prepare all the dependencies in this phase.
 0324            assetsToMark.AddRange(DumpImportableAssets(texturePaths));
 0325            DumpRawAssets(bufferPaths);
 326
 0327            GLTFImporter.OnGLTFRootIsConstructed -= ABConverter.Utils.FixGltfRootInvalidUriCharacters;
 0328            GLTFImporter.OnGLTFRootIsConstructed += ABConverter.Utils.FixGltfRootInvalidUriCharacters;
 329
 0330            foreach (var gltfPath in gltfPaths)
 331            {
 0332                assetsToMark.Add(DumpGltf(gltfPath, texturePaths, bufferPaths));
 333            }
 334
 0335            env.assetDatabase.Refresh();
 0336            env.assetDatabase.SaveAssets();
 337
 0338            MarkAllAssetBundles(assetsToMark);
 0339            MarkShaderAssetBundle();
 340
 0341            return true;
 342        }
 343
 344        /// <summary>
 345        /// Trims off existing asset bundles from the given AssetPath array,
 346        /// if none exists and shouldAbortBecauseAllBundlesExist is true, it will return false.
 347        /// </summary>
 348        /// <param name="gltfPaths">paths to be checked for existence</param>
 349        /// <returns>false if all paths are already converted to asset bundles, true if the conversion makes sense</retu
 350        internal bool FilterDumpList(ref List<AssetPath> gltfPaths)
 351        {
 352            bool shouldBuildAssetBundles;
 353
 0354            totalAssets += gltfPaths.Count;
 355
 0356            if (settings.skipAlreadyBuiltBundles)
 357            {
 0358                int gltfCount = gltfPaths.Count;
 359
 0360                gltfPaths = gltfPaths.Where(
 361                        assetPath =>
 0362                            !env.file.Exists(settings.finalAssetBundlePath + assetPath.hash))
 363                    .ToList();
 364
 0365                int skippedCount = gltfCount - gltfPaths.Count;
 0366                skippedAssets += skippedCount;
 0367                shouldBuildAssetBundles = gltfPaths.Count == 0;
 0368            }
 369            else
 370            {
 0371                shouldBuildAssetBundles = false;
 372            }
 373
 0374            if (shouldBuildAssetBundles)
 375            {
 0376                log.Info("All assets in this scene were already generated!. Skipping.");
 0377                return false;
 378            }
 379
 0380            return true;
 381        }
 382
 383        /// <summary>
 384        /// Dump a single gltf asset injecting the proper external references
 385        /// </summary>
 386        /// <param name="gltfPath">GLTF to be dumped</param>
 387        /// <param name="texturePaths">array with texture dependencies</param>
 388        /// <param name="bufferPaths">array with buffer dependencies</param>
 389        /// <returns>gltf AssetPath if dump succeeded, null if don't</returns>
 390        internal AssetPath DumpGltf(AssetPath gltfPath, List<AssetPath> texturePaths, List<AssetPath> bufferPaths)
 391        {
 0392            List<Stream> streamsToDispose = new List<Stream>();
 393
 0394            PersistentAssetCache.ImageCacheByUri.Clear();
 0395            PersistentAssetCache.StreamCacheByUri.Clear();
 396
 0397            log.Verbose("Start injecting stuff into " + gltfPath.hash);
 398
 399            //NOTE(Brian): Prepare gltfs gathering its dependencies first and filling the importer's static cache.
 0400            foreach (var texturePath in texturePaths)
 401            {
 0402                log.Verbose("Injecting texture... " + texturePath.hash + " -> " + texturePath.finalPath);
 0403                RetrieveAndInjectTexture(gltfPath, texturePath);
 404            }
 405
 0406            foreach (var bufferPath in bufferPaths)
 407            {
 0408                log.Verbose("Injecting buffer... " + bufferPath.hash + " -> " + bufferPath.finalPath);
 0409                RetrieveAndInjectBuffer(gltfPath, bufferPath); // TODO: this adds buffers that will be used in the futur
 410            }
 411
 0412            log.Verbose("About to load " + gltfPath.hash);
 413
 414            //NOTE(Brian): Load the gLTF after the dependencies are injected.
 415            //             The GLTFImporter will use the PersistentAssetCache to resolve them.
 0416            string path = DownloadAsset(gltfPath);
 417
 0418            if (path != null)
 419            {
 0420                env.assetDatabase.Refresh();
 0421                env.assetDatabase.SaveAssets();
 422            }
 423
 0424            log.Verbose("End load " + gltfPath.hash);
 425
 0426            foreach (var streamDataKvp in PersistentAssetCache.StreamCacheByUri)
 427            {
 0428                if (streamDataKvp.Value.stream != null)
 0429                    streamsToDispose.Add(streamDataKvp.Value.stream);
 430            }
 431
 0432            foreach (var s in streamsToDispose)
 433            {
 0434                s.Dispose();
 435            }
 436
 0437            return path != null ? gltfPath : null;
 438        }
 439
 440        /// <summary>
 441        /// Download assets and put them in the working folder.
 442        /// </summary>
 443        /// <param name="bufferPaths">AssetPath list containing all the desired paths to be dumped</param>
 444        /// <returns>List of the successfully dumped assets.</returns>
 445        internal List<AssetPath> DumpRawAssets(List<AssetPath> bufferPaths)
 446        {
 0447            List<AssetPath> result = new List<AssetPath>(bufferPaths);
 448
 0449            if (bufferPaths.Count == 0 || bufferPaths == null)
 0450                return result;
 451
 0452            foreach (var assetPath in bufferPaths)
 453            {
 0454                if (env.file.Exists(assetPath.finalPath))
 455                    continue;
 456
 0457                var finalDlPath = DownloadAsset(assetPath);
 458
 0459                if (string.IsNullOrEmpty(finalDlPath))
 460                {
 0461                    result.Remove(assetPath);
 0462                    log.Error("Failed to get buffer dependencies! failing asset: " + assetPath.hash);
 463                }
 464            }
 465
 0466            return result;
 467        }
 468
 469        /// <summary>
 470        /// This will dump all assets contained in the AssetPath list using the baseUrl + hash.
 471        ///
 472        /// After the assets are dumped, they will be imported using Unity's AssetDatabase and
 473        /// their guids will be normalized using the asset's cid.
 474        ///
 475        /// The guid normalization will ensure the guids remain consistent and the same asset will
 476        /// always have the asset guid. If we don't normalize the guids, Unity will chose a random one,
 477        /// and this can break the Asset Bundles dependencies as they are resolved by guid.
 478        /// </summary>
 479        /// <param name="assetPaths">List of assetPaths to be dumped</param>
 480        /// <returns>A list with assetPaths that were successfully dumped. This list will be empty if all dumps failed.<
 481        internal List<AssetPath> DumpImportableAssets(List<AssetPath> assetPaths)
 482        {
 0483            List<AssetPath> result = new List<AssetPath>(assetPaths);
 484
 0485            foreach (var assetPath in assetPaths)
 486            {
 0487                if (env.file.Exists(assetPath.finalPath))
 488                    continue;
 489
 490                //NOTE(Brian): try to get an AB before getting the original texture, so we bind the dependencies correct
 0491                string fullPathToTag = DownloadAsset(assetPath);
 492
 0493                if (fullPathToTag == null)
 494                {
 0495                    result.Remove(assetPath);
 0496                    log.Error("Failed to get texture dependencies! failing asset: " + assetPath.hash);
 0497                    continue;
 498                }
 499
 0500                var importer = env.assetDatabase.GetImporterAtPath(assetPath.finalPath);
 501
 0502                if (importer is TextureImporter texImporter)
 503                {
 0504                    texImporter.crunchedCompression = true;
 0505                    texImporter.textureCompression = TextureImporterCompression.CompressedHQ;
 506
 0507                    ReduceTextureSizeIfNeeded(assetPath.hash + "/" + assetPath.hash + Path.GetExtension(assetPath.file),
 0508                }
 509                else
 510                {
 0511                    env.assetDatabase.ImportAsset(assetPath.finalPath, ImportAssetOptions.ForceUpdate);
 0512                    env.assetDatabase.SaveAssets();
 513                }
 514
 0515                SetDeterministicAssetDatabaseGuid(assetPath);
 516
 0517                log.Verbose($"Dumping file -> {assetPath}");
 518            }
 519
 0520            return result;
 521        }
 522
 523        private void ReduceTextureSizeIfNeeded(string texturePath, float maxSize)
 524        {
 0525            string finalTexturePath = finalDownloadedPath + texturePath;
 526
 0527            byte[] image = File.ReadAllBytes(finalTexturePath);
 528
 0529            var tmpTex = new Texture2D(1, 1);
 530
 0531            if (!ImageConversion.LoadImage(tmpTex, image))
 0532                return;
 533
 0534            float factor = 1.0f;
 0535            int width = tmpTex.width;
 0536            int height = tmpTex.height;
 537
 0538            float maxTextureSize = maxSize;
 539
 0540            if (width < maxTextureSize && height < maxTextureSize)
 0541                return;
 542
 0543            if (width >= height)
 544            {
 0545                factor = (float)maxTextureSize / width;
 0546            }
 547            else
 548            {
 0549                factor = (float)maxTextureSize / height;
 550            }
 551
 0552            Texture2D dstTex = TextureHelpers.Resize(tmpTex, (int)(width * factor), (int)(height * factor));
 0553            byte[] endTex = ImageConversion.EncodeToPNG(dstTex);
 0554            UnityEngine.Object.DestroyImmediate(tmpTex);
 555
 0556            File.WriteAllBytes(finalTexturePath, endTex);
 557
 0558            AssetDatabase.ImportAsset(finalDownloadedAssetDbPath + texturePath, ImportAssetOptions.ForceUpdate);
 0559            AssetDatabase.SaveAssets();
 0560        }
 561
 562        /// <summary>
 563        /// This will download a single asset referenced by an AssetPath.
 564        /// The download target is baseUrl + hash.
 565        /// </summary>
 566        /// <param name="assetPath">The AssetPath object referencing the asset to be downloaded</param>
 567        /// <returns>The file output path. Null if download failed.</returns>
 568        internal string DownloadAsset(AssetPath assetPath)
 569        {
 0570            string outputPath = assetPath.finalPath;
 0571            string outputPathDir = Path.GetDirectoryName(outputPath);
 0572            string finalUrl = settings.baseUrl + assetPath.hash;
 573
 0574            if (env.file.Exists(outputPath))
 575            {
 0576                log.Verbose("Skipping already generated asset: " + outputPath);
 0577                return outputPath;
 578            }
 579
 0580            DownloadHandler downloadHandler = null;
 581
 582            try
 583            {
 0584                downloadHandler = env.webRequest.Get(finalUrl);
 585
 0586                if (downloadHandler == null)
 587                {
 0588                    log.Error($"Download failed! {finalUrl} -- null DownloadHandler");
 0589                    return null;
 590                }
 0591            }
 0592            catch (HttpRequestException e)
 593            {
 0594                log.Error($"Download failed! {finalUrl} -- {e.Message}");
 0595                return null;
 596            }
 597
 0598            byte[] assetData = downloadHandler.data;
 0599            downloadHandler.Dispose();
 600
 0601            log.Verbose($"Downloaded asset = {finalUrl} to {outputPath}");
 602
 0603            if (!env.directory.Exists(outputPathDir))
 0604                env.directory.CreateDirectory(outputPathDir);
 605
 0606            env.file.WriteAllBytes(outputPath, assetData);
 607
 0608            env.assetDatabase.ImportAsset(outputPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.Imp
 609
 0610            return outputPath;
 0611        }
 612
 613        /// <summary>
 614        /// Load dumped textures and put them in PersistentAssetCache so the GLTFSceneImporter
 615        /// can pick them up.
 616        /// </summary>
 617        /// <param name="gltfPath">GLTF path of the gltf that will pick up the references</param>
 618        /// <param name="texturePath">Texture path of the texture to be injected</param>
 619        internal void RetrieveAndInjectTexture(AssetPath gltfPath, AssetPath texturePath)
 620        {
 0621            string finalPath = texturePath.finalPath;
 622
 0623            if (!env.file.Exists(finalPath))
 0624                return;
 625
 0626            Texture2D t2d = env.assetDatabase.LoadAssetAtPath<Texture2D>(finalPath);
 627
 0628            if (t2d == null)
 0629                return;
 630
 0631            string relativePath = ABConverter.PathUtils.GetRelativePathTo(gltfPath.file, texturePath.file);
 632
 633            //NOTE(Brian): This cache will be used by the GLTF importer when seeking textures. This way the importer wil
 634            //             consume the asset bundle dependencies instead of trying to create new textures.
 0635            PersistentAssetCache.AddImage(relativePath, gltfPath.finalPath, t2d);
 0636        }
 637
 638        /// <summary>
 639        /// Load dumped buffers and put them in PersistentAssetCache so the GLTFSceneImporter
 640        /// can pick them up.
 641        /// </summary>
 642        /// <param name="gltfPath">GLTF path of the gltf that will pick up the references</param>
 643        /// <param name="bufferPath">Buffer path of the texture to be injected</param>
 644        internal void RetrieveAndInjectBuffer(AssetPath gltfPath, AssetPath bufferPath)
 645        {
 0646            string finalPath = bufferPath.finalPath;
 647
 0648            if (!env.file.Exists(finalPath))
 0649                return;
 650
 0651            Stream stream = env.file.OpenRead(finalPath);
 0652            string relativePath = ABConverter.PathUtils.GetRelativePathTo(gltfPath.file, bufferPath.file);
 653
 654            // NOTE(Brian): This cache will be used by the GLTF importer when seeking streams. This way the importer wil
 655            //              consume the asset bundle dependencies instead of trying to create new streams.
 0656            PersistentAssetCache.AddBuffer(relativePath, gltfPath.finalPath, stream);
 0657        }
 658
 659        /// <summary>
 660        /// Mark all the given assetPaths to be built as asset bundles by Unity's BuildPipeline.
 661        /// </summary>
 662        /// <param name="assetPaths">The paths to be built.</param>
 663        private void MarkAllAssetBundles(List<AssetPath> assetPaths)
 664        {
 0665            foreach (var assetPath in assetPaths)
 666            {
 0667                ABConverter.Utils.MarkFolderForAssetBundleBuild(assetPath.finalPath, assetPath.hash);
 668            }
 0669        }
 670
 671        /// <summary>
 672        /// Build all marked paths as asset bundles using Unity's BuildPipeline and generate their .depmap files
 673        /// </summary>
 674        /// <param name="manifest">AssetBundleManifest generated by the build.</param>
 675        /// <returns>true is build was successful</returns>
 676        public virtual bool BuildAssetBundles(out AssetBundleManifest manifest)
 677        {
 0678            env.assetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate | Impor
 0679            env.assetDatabase.SaveAssets();
 680
 0681            env.assetDatabase.MoveAsset(finalDownloadedPath, Config.DOWNLOADED_PATH_ROOT);
 682
 0683            manifest = env.buildPipeline.BuildAssetBundles(settings.finalAssetBundlePath, BuildAssetBundleOptions.Uncomp
 684
 0685            if (manifest == null)
 686            {
 0687                log.Error("Error generating asset bundle!");
 0688                return false;
 689            }
 690
 0691            DependencyMapBuilder.Generate(env.file, settings.finalAssetBundlePath, hashLowercaseToHashProper, manifest, 
 0692            logBuffer += $"Generating asset bundles at path: {settings.finalAssetBundlePath}\n";
 693
 0694            string[] assetBundles = manifest.GetAllAssetBundles();
 695
 0696            logBuffer += $"Total generated asset bundles: {assetBundles.Length}\n";
 697
 0698            for (int i = 0; i < assetBundles.Length; i++)
 699            {
 0700                if (string.IsNullOrEmpty(assetBundles[i]))
 701                    continue;
 702
 0703                logBuffer += $"#{i} Generated asset bundle name: {assetBundles[i]}\n";
 704            }
 705
 0706            logBuffer += $"\nFree disk space after conv: {PathUtils.GetFreeSpace()}";
 0707            return true;
 708        }
 709
 710        /// <summary>
 711        /// Clean all working folders and end the batch process.
 712        /// </summary>
 713        /// <param name="errorCode">final errorCode of the conversion process</param>
 714        private void CleanAndExit(ErrorCodes errorCode)
 715        {
 0716            float conversionTime = Time.realtimeSinceStartup - startTime;
 0717            logBuffer = $"Conversion finished!. last error code = {errorCode}";
 718
 0719            logBuffer += "\n";
 0720            logBuffer += $"Converted {totalAssets - skippedAssets} of {totalAssets}. (Skipped {skippedAssets})\n";
 0721            logBuffer += $"Total time: {conversionTime}";
 722
 0723            if (totalAssets > 0)
 724            {
 0725                logBuffer += $"... Time per asset: {conversionTime / totalAssets}\n";
 726            }
 727
 0728            logBuffer += "\n";
 0729            logBuffer += logBuffer;
 730
 0731            log.Info(logBuffer);
 732
 0733            CleanupWorkingFolders();
 0734            Utils.Exit((int) errorCode);
 0735        }
 736
 737        /// <summary>
 738        /// in asset bundles, all dependencies are resolved by their guid (and not the AB hash nor CRC)
 739        /// So to ensure dependencies are being kept in subsequent editor runs we normalize the asset guid using
 740        /// the CID.
 741        ///
 742        /// This method:
 743        /// - Looks for the meta file of the given assetPath.
 744        /// - Changes the .meta guid using the assetPath's cid as seed.
 745        /// - Does some file system gymnastics to make sure the new guid is imported to our AssetDatabase.
 746        /// </summary>
 747        /// <param name="assetPath">AssetPath of the target asset to modify</param>
 748        private void SetDeterministicAssetDatabaseGuid(AssetPath assetPath)
 749        {
 0750            string metaPath = env.assetDatabase.GetTextMetaFilePathFromAssetPath(assetPath.finalPath);
 751
 0752            env.assetDatabase.ReleaseCachedFileHandles();
 753
 0754            string metaContent = env.file.ReadAllText(metaPath);
 0755            string guid = ABConverter.Utils.CidToGuid(assetPath.hash);
 0756            string newMetaContent = Regex.Replace(metaContent, @"guid: \w+?\n", $"guid: {guid}\n");
 757
 758            //NOTE(Brian): We must do this hack in order to the new guid to be added to the AssetDatabase.
 759            //             on windows, an AssetImporter.SaveAndReimport call makes the trick, but this won't work
 760            //             on Unix based OSes for some reason.
 0761            env.file.Delete(metaPath);
 762
 0763            env.file.Copy(assetPath.finalPath, finalDownloadedPath + "tmp");
 0764            env.assetDatabase.DeleteAsset(assetPath.finalPath);
 0765            env.file.Delete(assetPath.finalPath);
 766
 0767            env.assetDatabase.Refresh();
 0768            env.assetDatabase.SaveAssets();
 769
 0770            env.file.Copy(finalDownloadedPath + "tmp", assetPath.finalPath);
 0771            env.file.WriteAllText(metaPath, newMetaContent);
 0772            env.file.Delete(finalDownloadedPath + "tmp");
 773
 0774            env.assetDatabase.Refresh();
 0775            env.assetDatabase.SaveAssets();
 0776        }
 777
 0778        internal void CleanAssetBundleFolder(string[] assetBundles) { ABConverter.Utils.CleanAssetBundleFolder(env.file,
 779
 780        internal void PopulateLowercaseMappings(ContentServerUtils.MappingPair[] pairs)
 781        {
 0782            foreach (var content in pairs)
 783            {
 0784                string hashLower = content.hash.ToLowerInvariant();
 785
 0786                if (!hashLowercaseToHashProper.ContainsKey(hashLower))
 0787                    hashLowercaseToHashProper.Add(hashLower, content.hash);
 788            }
 0789        }
 790
 791        /// <summary>
 792        /// This method tags the main shader, so all the asset bundles don't contain repeated shader assets.
 793        /// This way we save the big Shader.Parse and gpu compiling performance overhead and make
 794        /// the bundles a bit lighter.
 795        /// </summary>
 796        private void MarkShaderAssetBundle()
 797        {
 798            //NOTE(Brian): The shader asset bundle that's going to be generated doesn't need to be really used,
 799            //             as we are going to use the embedded one, so we are going to just delete it after the
 800            //             generation ended.
 0801            var mainShader = Shader.Find("DCL/Universal Render Pipeline/Lit");
 0802            ABConverter.Utils.MarkAssetForAssetBundleBuild(env.assetDatabase, mainShader, MAIN_SHADER_AB_NAME);
 0803        }
 804
 805        internal virtual void InitializeDirectoryPaths(bool deleteIfExists)
 806        {
 0807            log.Info("Initializing directory -- " + finalDownloadedPath);
 0808            env.directory.InitializeDirectory(finalDownloadedPath, deleteIfExists);
 0809            log.Info("Initializing directory -- " + settings.finalAssetBundlePath);
 0810            env.directory.InitializeDirectory(settings.finalAssetBundlePath, deleteIfExists);
 0811        }
 812
 813        internal void CleanupWorkingFolders()
 814        {
 0815            env.file.Delete(settings.finalAssetBundlePath + Config.ASSET_BUNDLE_FOLDER_NAME);
 0816            env.file.Delete(settings.finalAssetBundlePath + Config.ASSET_BUNDLE_FOLDER_NAME + ".manifest");
 817
 0818            if (settings.deleteDownloadPathAfterFinished)
 819            {
 0820                env.directory.Delete(finalDownloadedPath);
 0821                env.assetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
 822            }
 0823        }
 824    }
 825}

Methods/Properties

step()
step(DCL.ABConverter.Core/State/Step)
lastErrorCode()
lastErrorCode(DCL.ABConverter.Core/ErrorCodes)
Core(DCL.ABConverter.Environment, DCL.ABConverter.Client/Settings)
Core()
Convert(DCL.ContentServerUtils/MappingPair[], System.Action[ErrorCodes])
ConvertDumpedAssets(System.Action[ErrorCodes])
ProcessSkippedAssets(System.Int32)
GetAssetDependenciesMappingPairs(System.String, System.String, System.String, System.Collections.Generic.List`1[[DCL.ContentServerUtils/MappingPair, ContentServerUtils, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]&)
DumpAssets(DCL.ContentServerUtils/MappingPair[])
FilterDumpList(System.Collections.Generic.List`1[[DCL.ABConverter.AssetPath, ABConverter, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]&)
DumpGltf(DCL.ABConverter.AssetPath, System.Collections.Generic.List[AssetPath], System.Collections.Generic.List[AssetPath])
DumpRawAssets(System.Collections.Generic.List[AssetPath])
DumpImportableAssets(System.Collections.Generic.List[AssetPath])
ReduceTextureSizeIfNeeded(System.String, System.Single)
DownloadAsset(DCL.ABConverter.AssetPath)
RetrieveAndInjectTexture(DCL.ABConverter.AssetPath, DCL.ABConverter.AssetPath)
RetrieveAndInjectBuffer(DCL.ABConverter.AssetPath, DCL.ABConverter.AssetPath)
MarkAllAssetBundles(System.Collections.Generic.List[AssetPath])
BuildAssetBundles(UnityEngine.AssetBundleManifest&)
CleanAndExit(DCL.ABConverter.Core/ErrorCodes)
SetDeterministicAssetDatabaseGuid(DCL.ABConverter.AssetPath)
CleanAssetBundleFolder(System.String[])
PopulateLowercaseMappings(DCL.ContentServerUtils/MappingPair[])
MarkShaderAssetBundle()
InitializeDirectoryPaths(System.Boolean)
CleanupWorkingFolders()