private IEnumerator <Null> RefreshAssetSet(PolySetType type) { List <AssetDetails> models = new List <AssetDetails>(); // When the list is empty, make it the actual list acted upon so that results start // showing up immediately. if (m_AssetSetByType[type].m_Models.Count == 0) { m_AssetSetByType[type].m_Models = models; } AssetLister lister = VrAssetService.m_Instance.ListAssets(type); while (lister.HasMore || models.Count == 0) { using (var cr = lister.NextPage(models, m_ThumbnailSuffix)) { while (true) { try { if (!cr.MoveNext()) { break; } } catch (VrAssetServiceException e) { ControllerConsoleScript.m_Instance.AddNewLine(e.Message); Debug.LogException(e); yield break; } yield return(cr.Current); } } if (models.Count == 0) { break; } } // As the assets may already have models loaded into them, just add any new models and // remove old ones. var newIds = new HashSet <string>(models.Select(m => m.AssetId)); var oldIds = new HashSet <string>(m_AssetSetByType[type].m_Models.Select(m => m.AssetId)); // These must be reified; if they are left as lazy IEnumerables, O(n^2) behavior results HashSet <string> toAdd = SetMinus(newIds, oldIds); HashSet <string> toRemove = SetMinus(oldIds, newIds); m_AssetSetByType[type].m_Models.RemoveAll(m => toRemove.Contains(m.AssetId)); m_AssetSetByType[type].m_Models.InsertRange(0, models.Where(m => toAdd.Contains(m.AssetId))); if (CatalogChanged != null) { CatalogChanged(); } }
// Fetch asset metadata from server and populate m_Sketches private IEnumerator PopulateSketchesCoroutine() { // Replacement data structures that will be populated with incoming data. These // temporary structures will be copied to the "live" structures in chunks, so // beware of modifications that may reference incomplete data. List <PolySketch> sketches = new List <PolySketch>(); Dictionary <string, PolySketch> assetIds = new Dictionary <string, PolySketch>(); bool fromEmpty = m_AssetIds.Count == 0; int loadSketchCount = 0; AssetLister lister = null; List <PolySceneFileInfo> infos = new List <PolySceneFileInfo>(); // If we don't have a connection to Poly and we're querying the Showcase, use // the json metadatas stored in resources, instead of trying to get them from Poly. if (VrAssetService.m_Instance.NoConnection && m_Type == SketchSetType.Curated) { TextAsset[] textAssets = Resources.LoadAll <TextAsset>(SketchCatalog.kDefaultShowcaseSketchesFolder); for (int i = 0; i < textAssets.Length; ++i) { JObject jo = JObject.Parse(textAssets[i].text); infos.Add(new PolySceneFileInfo(jo)); } } else { lister = m_AssetService.ListAssets(Type); } bool changed = false; int pagesFetched = 0; while (lister == null || lister.HasMore || assetIds.Count == 0) { if (sketches.Count >= 180) { break; // Don't allow the sketchbook to become too big. } // lister will be null if we can't connect to Poly and we've pre-populated infos // with data from Resources. if (lister != null) { using (var cr = lister.NextPage(infos)) { while (true) { try { if (!cr.MoveNext()) { break; } } catch (VrAssetServiceException e) { ControllerConsoleScript.m_Instance.AddNewLine(e.UserFriendly); Debug.LogException(e); yield break; } yield return(cr.Current); } } } if (infos.Count == 0) { break; } if (m_CacheDir == null) { yield break; } // Only cull the curated sketches. If a user likes a sketch that's very high poly count, // there's no feedback to tell them why it didn't show up in their list. b/123649719 if (m_Type == SketchSetType.Curated) { infos = infos.Where(x => x.GltfTriangleCount < m_MaximumSceneTriangles).ToList(); } if (m_Type == SketchSetType.Curated && !assetIds.Keys.Contains(kIntroSketchAssetId)) { yield return(VrAssetService.m_Instance.InsertSketchInfo( kIntroSketchAssetId, kIntroSketchIndex, infos)); } for (int i = 0; i < infos.Count; i++) { PolySceneFileInfo info = infos[i]; PolySketch sketch; if (m_AssetIds.TryGetValue(info.AssetId, out sketch)) { // We already have this sketch } else { sketch = new PolySketch(info); sketch.m_DownloadIndex = loadSketchCount++; // Set local paths info.TiltPath = Path.Combine(m_CacheDir, String.Format("{0}.tilt", info.AssetId)); info.IconPath = Path.Combine(m_CacheDir, String.Format("{0}.png", info.AssetId)); changed = true; } if (assetIds.ContainsKey(info.AssetId)) { Debug.LogErrorFormat("VR Asset Service has returned two objects with asset id '{0}'.", info.AssetId); } else { sketches.Add(sketch); assetIds.Add(info.AssetId, sketch); } } infos.Clear(); // Only download the files with every other page, otherwise the sketch pages change too often, // Which results in a bad user experience. if ((++pagesFetched & 1) == 0 || lister == null || !lister.HasMore) { if (Type == SketchSetType.Curated) { sketches.Sort(CompareSketchesByTriangleCountAndDownloadIndex); } // If populating the set from new then show stuff as it comes in. // (We don't have to worry about anything being removed) if (fromEmpty) { yield return(DownloadFilesCoroutine(sketches)); RemoveFailedDownloads(sketches); // Copying sketches to m_Sketches before sketches has completed populating is a bit // dangerous, but as long as they're copied and then listeners are notified // immediately afterward with OnChanged(), there data should be stable. m_TotalCount = sketches.Count; m_Sketches = new List <PolySketch>(sketches); m_AssetIds = assetIds; m_Ready = true; OnChanged(); } } } if (!fromEmpty) { // Find anything that was removed int removed = m_Sketches.Count(s => !assetIds.ContainsKey(s.PolySceneFileInfo.AssetId)); if (removed > 0) { changed = true; } // Download new files before we notify our listeners that we've got new stuff for them. if (changed) { yield return(DownloadFilesCoroutine(sketches)); RemoveFailedDownloads(sketches); } // DeleteOldSketchesCoroutine relies on m_AssetIds being up to date, so set these before // we try to cull the herd. m_AssetIds = assetIds; if (changed) { yield return(DeleteOldSketchesCoroutine()); } // Set new data live m_TotalCount = sketches.Count; m_Sketches = new List <PolySketch>(sketches); m_Ready = true; if (changed) { OnChanged(); } } else { // On first run populate, take the time to clean out any cobwebs. // Note that this is particularly important for the curated sketches, which do not have // a periodic refresh, meaning old sketches will never been deleted in the other path. yield return(DeleteOldSketchesCoroutine()); OnChanged(); } }