/// Returns all non-null asset ids from the passed sketch's metadata. /// null return value means "empty list". /// Raises exception on error. private static List<string> GetModelIds(SceneFileInfo sceneFileInfo) { // Json deserializing is in a separate method that doesn't access Unity objects so that it // can be called on a thread. The json deserializing can be pretty slow and can cause // frame drops if performed on the main thread. Stream metadata = SaveLoadScript.GetMetadataReadStream(sceneFileInfo); if (metadata == null) { if (sceneFileInfo.Exists) { // ??? Let's try to provoke an exception to propagate to the caller using (var dummy = File.OpenRead(sceneFileInfo.FullPath)) {} throw new Exception($"Unknown error opening metadata {sceneFileInfo.FullPath}"); } else { throw new Exception( "Reading metadata from nonexistent " + $"{sceneFileInfo.InfoType} {sceneFileInfo.HumanName}"); } } using (var jsonReader = new JsonTextReader(new StreamReader(metadata))) { var jsonData = SaveLoadScript.m_Instance.DeserializeMetadata(jsonReader); if (SaveLoadScript.m_Instance.LastMetadataError != null) { throw new Exception($"Deserialize error: {SaveLoadScript.m_Instance.LastMetadataError}"); } if (jsonData.ModelIndex == null) { return null; } return jsonData.ModelIndex.Select(m => m.AssetId).Where(a => a != null).ToList(); } }
/// Waits for the json data to be read on a background thread, and then executes a precache /// coroutine for each found asset. private IEnumerator<Null> PrecacheModelsCoroutine(SceneFileInfo sceneFileInfo, string reason) { var getIdsFuture = new Future<List<string>>(() => GetModelIds(sceneFileInfo)); List<string> ids; while (true) { try { if (getIdsFuture.TryGetResult(out ids)) { break; } } catch (FutureFailed e) { throw new Exception($"While reading {sceneFileInfo}", e); } yield return null; } if (ids == null) { yield break; } List<IEnumerator<Null>> precacheCoroutines = new List<IEnumerator<Null>>(); // Only trigger off one precache routine per frame. foreach (string id in ids) { if (m_ModelsByAssetId.ContainsKey(id)) { // Already cached continue; } if (! FileUtils.InitializeDirectory(GetCacheDirectoryForAsset(id))) { continue; } precacheCoroutines.Add(PrecacheCoroutine( VrAssetService.m_Instance.GetAsset(id, VrAssetFormat.GLTF2, reason))); yield return null; } var cr = CoroutineUtil.CompleteAllCoroutines(precacheCoroutines); while (cr.MoveNext()) { yield return cr.Current; } }
/// Downloads models referenced by the passed sketch. /// Pass the reason this is happening. /// TODO: maybe annotate the download request so we can choose whether they turn /// into model loads? public void PrecacheModels(SceneFileInfo sceneFileInfo, string reason) { if (string.IsNullOrEmpty(App.Config.GoogleSecrets?.ApiKey)) { return; } StartCoroutine(PrecacheModelsCoroutine(sceneFileInfo, reason)); }
public void UpdateHumanNameFrom(SceneFileInfo sourceSceneFileInfo) { // Copy over relevant parts of the original scene file info. if (sourceSceneFileInfo != null) { m_humanName = sourceSceneFileInfo.HumanName; } }
/// Synchronously read thumbnail. Returns null on error. public static byte[] ReadThumbnail(SceneFileInfo fileinfo) { using (Stream s = fileinfo.GetReadStream(TiltFile.FN_THUMBNAIL)) { if (s == null) { return(null); } byte[] buffer = new byte[32 * 1024]; MemoryStream ms = new MemoryStream(); while (true) { int read = s.Read(buffer, 0, buffer.Length); if (read == 0) { return(ms.ToArray()); } ms.Write(buffer, 0, read); } } }
void RefreshDetails() { m_MenuButton.SetSketchDetails(m_SketchIndex, SketchSet.Type); m_SizeOk = true; if (m_SketchSet.Type == SketchSetType.Liked) { if (m_SketchSet.IsSketchIndexValid(m_SketchIndex)) { SceneFileInfo sfi = m_SketchSet.GetSketchSceneFileInfo(m_SketchIndex); if (sfi.TriangleCount is int triCount) { m_SizeOk = triCount < QualityControls.m_Instance.AppQualityLevels.MaxPolySketchTriangles; } } } if (m_Warning != null) { m_Warning.GetComponent <Renderer>().material = m_SizeOk ? m_WarningMaterial : m_ErrorMaterial; } }
// UpdateIcons() is called repeatedly by Update() until these three conditions are met: // 1: The SketchSet has loaded all the requested icons // 2: The textures for all the buttons have been set // 3: (Cloud only) The SketchSet has downloaded the corresponding .tilt files. // Until the .tilt file is downloaded we set a fade on the button, and need to keep updating // until the file is downloaded. private void UpdateIcons() { m_AllIconTexturesAssigned = true; m_AllSketchesAreAvailable = true; // Poll sketch catalog until icons have loaded foreach (BaseButton baseButton in Icons) { LoadSketchButton icon = baseButton as LoadSketchButton; if (icon == null) { continue; } int iSketchIndex = icon.SketchIndex; if (m_SketchSet.IsSketchIndexValid(iSketchIndex)) { icon.FadeIn = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex).Available ? 1f : 0.5f; if (!icon.ThumbnailLoaded) { Texture2D rTexture = null; string[] authors; string description; if (m_SketchSet.GetSketchIcon(iSketchIndex, out rTexture, out authors, out description)) { if (rTexture != null) { // Pass through aspect ratio of image so we don't get squished // thumbnails from Poly m_ImageAspect = (float)rTexture.width / rTexture.height; float aspect = m_ImageAspect; icon.SetButtonTexture(rTexture, aspect); } else { icon.SetButtonTexture(m_UnknownImageTexture); } // Mark the texture as assigned regardless of actual bits being valid icon.ThumbnailLoaded = true; ; List <string> lines = new List <string>(); lines.Add(icon.Description); SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); if (info is PolySceneFileInfo polyInfo && polyInfo.License != VrAssetService.kCreativeCommonsLicense) { lines.Add(String.Format("© {0}", authors[0])); lines.Add("All Rights Reserved"); } else { // Include primary author in description if available if (authors != null && authors.Length > 0) { lines.Add(authors[0]); } // Include an actual description if (description != null) { lines.Add(App.ShortenForDescriptionText(description)); } } icon.SetDescriptionText(lines.ToArray()); } else { // While metadata has not finished loading, check if this file is valid bool bFileValid = false; SceneFileInfo rInfo = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); if (rInfo != null) { bFileValid = rInfo.Exists; } // If this file isn't valid, just keep the defaults and move on if (!bFileValid) { icon.SetButtonTexture(m_UnknownImageTexture); icon.ThumbnailLoaded = true; } else { m_AllIconTexturesAssigned = false; } if (!rInfo.Available) { m_AllSketchesAreAvailable = false; } } } } }
protected override void RefreshPage() { m_SketchSet.RequestOnlyLoadedMetadata(GetIconLoadIndices()); m_AllIconTexturesAssigned = false; m_AllSketchesAreAvailable = false; // Disable all. foreach (var i in m_IconsOnFirstPage) { i.SetActive(false); } foreach (var i in m_IconsOnNormalPage) { i.SetActive(false); } // Base Refresh updates the modal parts of the panel, and we always want those refreshed. base.RefreshPage(); bool polyDown = VrAssetService.m_Instance.NoConnection && (m_CurrentSketchSet == SketchSetType.Curated || m_CurrentSketchSet == SketchSetType.Liked); m_NoPolyConnectionMessage.SetActive(polyDown); bool outOfDate = !polyDown && !VrAssetService.m_Instance.Available && (m_CurrentSketchSet == SketchSetType.Curated || m_CurrentSketchSet == SketchSetType.Liked); m_OutOfDateMessage.SetActive(outOfDate); if (outOfDate || polyDown) { m_NoSketchesMessage.SetActive(false); m_NoDriveSketchesMessage.SetActive(false); m_NotLoggedInMessage.SetActive(false); m_NoLikesMessage.SetActive(false); m_ContactingServerMessage.SetActive(false); m_NoShowcaseMessage.SetActive(false); return; } bool refreshIcons = m_SketchSet.NumSketches > 0; // Show no sketches if we don't have sketches. m_NoSketchesMessage.SetActive( (m_CurrentSketchSet == SketchSetType.User) && (m_SketchSet.NumSketches <= 0)); m_NoDriveSketchesMessage.SetActive( (m_CurrentSketchSet == SketchSetType.Drive) && (m_SketchSet.NumSketches <= 0)); // Show sign in popup if signed out for liked or drive sketchsets bool showNotLoggedIn = !App.GoogleIdentity.LoggedIn && (m_CurrentSketchSet == SketchSetType.Liked || m_CurrentSketchSet == SketchSetType.Drive); refreshIcons = refreshIcons && !showNotLoggedIn; m_NotLoggedInMessage.SetActive(showNotLoggedIn && m_CurrentSketchSet == SketchSetType.Liked); m_NotLoggedInDriveMessage.SetActive(showNotLoggedIn && m_CurrentSketchSet == SketchSetType.Drive); // Show no likes text & gallery button if we don't have liked sketches. m_NoLikesMessage.SetActive( (m_CurrentSketchSet == SketchSetType.Liked) && (m_SketchSet.NumSketches <= 0) && !m_SketchSet.IsActivelyRefreshingSketches && App.GoogleIdentity.LoggedIn); // Show Contacting Server if we're talking to Poly. m_ContactingServerMessage.SetActive( (m_CurrentSketchSet == SketchSetType.Curated || m_CurrentSketchSet == SketchSetType.Liked || m_CurrentSketchSet == SketchSetType.Drive) && (m_SketchSet.NumSketches <= 0) && (m_SketchSet.IsActivelyRefreshingSketches && App.GoogleIdentity.LoggedIn)); // Show Showcase error if we're in Showcase and don't have sketches. m_NoShowcaseMessage.SetActive( (m_CurrentSketchSet == SketchSetType.Curated) && (m_SketchSet.NumSketches <= 0) && !m_SketchSet.IsActivelyRefreshingSketches); // Refresh all icons if necessary. if (!refreshIcons) { return; } for (int i = 0; i < Icons.Count; i++) { LoadSketchButton icon = Icons[i] as LoadSketchButton; // Default to loading image icon.SetButtonTexture(m_LoadingImageTexture); icon.ThumbnailLoaded = false; // Set sketch index relative to page based index int iSketchIndex = m_IndexOffset + i; if (iSketchIndex >= m_SketchSet.NumSketches) { iSketchIndex = -1; } icon.SketchIndex = iSketchIndex; icon.ResetScale(); // Init icon according to availability of sketch GameObject go = icon.gameObject; if (m_SketchSet.IsSketchIndexValid(iSketchIndex)) { string sSketchName = m_SketchSet.GetSketchName(iSketchIndex); icon.SetDescriptionText(App.ShortenForDescriptionText(sSketchName)); SceneFileInfo info = m_SketchSet.GetSketchSceneFileInfo(iSketchIndex); if (info.Available) { m_SketchSet.PrecacheSketchModels(iSketchIndex); } if (info.TriangleCount is int triCount) { icon.WarningVisible = triCount > QualityControls.m_Instance.AppQualityLevels.WarningPolySketchTriangles; } else { icon.WarningVisible = false; } go.SetActive(true); } else { go.SetActive(false); } } }