// When we sort the sketches, we sort them into buckets while retaining order within those // buckets. We bucket the sketches using the gltf triangle count, but how we bucket depends on // whether we are sorting the liked sketches or the curated sketches. // Liked sketches get split into normal, complex (requires a warning), and impossible. // Curated sketches get bucketed by the nearest 100,000 triangles. private static int CompareSketchesByTriangleCountAndDownloadIndex(PolySketch a, PolySketch b) { int compareResult = CloudSketchComplexityBucket(a).CompareTo(CloudSketchComplexityBucket(b)); // If both sketches are in the same grouping, sort them relative to download index. if (compareResult == 0) { return(a.m_DownloadIndex.CompareTo(b.m_DownloadIndex)); } return(compareResult); }
public bool GetSketchIcon(int iSketchIndex, out Texture2D icon, out string[] authors, out string description) { description = null; if (iSketchIndex >= m_Sketches.Count) { icon = null; authors = null; return(false); } PolySketch sketch = m_Sketches[iSketchIndex]; icon = sketch.Icon; authors = sketch.Authors; return(icon != null); }
// Read the icon textures for all sketches in m_RequestedIcons private IEnumerator TextureLoaderCoroutine() { while (m_RequestedIcons.Count > 0) { foreach (int i in m_RequestedIcons) { PolySketch sketch = m_Sketches[i]; string path = sketch.PolySceneFileInfo.IconPath; if (sketch.PolySceneFileInfo.IconDownloaded) { byte[] data = File.ReadAllBytes(path); Texture2D t = new Texture2D(2, 2); t.LoadImage(data); sketch.Icon = t; } yield return(null); } m_RequestedIcons.RemoveAll(i => m_Sketches[i].Icon != null); } }
// Buckets the sketches into buckets 100000 tris in size. private static int CloudSketchComplexityBucket(PolySketch s) { return(s.PolySceneFileInfo.GltfTriangleCount / 100000); }
// 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(); } }