Example #1
0
        /// <summary>
        /// Callback invoked when we get request results.
        /// </summary>
        private void OnRequestResult(PolyStatusOr <PolyListAssetsResult> result)
        {
            if (result.Ok)
            {
                PtDebug.LogFormat("ABM: request results received ({0} assets).", result.Value.assets.Count);
                this.listAssetsResult = result;
                resultHasMorePages    = result.Value.nextPageToken != null;
            }
            else
            {
                Debug.LogError("Asset request failed. Try again later: " + result.Status);
                this.listAssetsResult = result;
            }
            querying = false;
            if (null != refreshCallback)
            {
                refreshCallback();
            }

            if (result.Ok)
            {
                assetsInUse = GetAssetsInUse();
                FinishFetchingThumbnails(result);
            }
        }
Example #2
0
        /// <summary>
        /// Called when sign in finishes.
        /// </summary>
        /// <param name="wasInteractive">If true, this was the interactive (browser-based) sign-in flow.</param>
        /// <param name="status">The result of the sign in process.</param>
        private void OnSignInFinished(bool wasInteractive, PolyStatus status)
        {
            if (status.ok)
            {
                string tok = PolyApi.AccessToken;
                PtDebug.LogFormat("ABM: Sign in success. Access token: {0}",
                                  (tok != null && tok.Length > 6) ? tok.Substring(0, 6) + "..." : "INVALID");
                PtAnalytics.SendEvent(PtAnalytics.Action.ACCOUNT_SIGN_IN_SUCCESS);
            }
            else if (wasInteractive)
            {
                Debug.LogErrorFormat("Failed to sign in. Please try again: " + status);
                PtAnalytics.SendEvent(PtAnalytics.Action.ACCOUNT_SIGN_IN_FAILURE, status.ToString());
            }
            if (null != refreshCallback)
            {
                refreshCallback();
            }

            // If we had a deferred request that was waiting for auth, send it now.
            if (requestToSendAfterAuth != null)
            {
                PtDebug.Log("Sending deferred request that was waiting for auth.");
                PolyRequest request = requestToSendAfterAuth;
                requestToSendAfterAuth = null;
                StartRequest(request);
            }
        }
Example #3
0
        public void Setup(PolyCacheConfig config)
        {
            // Create all the buffer holders. They are all initially idle.
            for (int i = 0; i < MAX_CONCURRENT_DOWNLOADS; i++)
            {
                idleBuffers.Add(new BufferHolder());
            }

            // Caching is only supported on Windows/Mac for now.
            bool cacheSupported = Application.platform == RuntimePlatform.WindowsEditor ||
                                  Application.platform == RuntimePlatform.WindowsPlayer ||
                                  Application.platform == RuntimePlatform.OSXEditor ||
                                  Application.platform == RuntimePlatform.OSXPlayer;

            PtDebug.LogFormat("Platform: {0}, cache supported: {1}", Application.platform, cacheSupported);

            if (cacheSupported && config.cacheEnabled)
            {
                //string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
                //string defaultCachePath = Path.Combine(Path.Combine(Path.Combine(
                //  appDataPath, Application.companyName), Application.productName), "WebRequestCache");
                string defaultCachePath = Path.Combine(Application.persistentDataPath, "WebRequestCache");

                string cachePath = config.cachePathOverride;
                if (string.IsNullOrEmpty(cachePath))
                {
                    cachePath = defaultCachePath;
                }
                // Note: Directory.CreateDirectory creates all directories in the path.
                Directory.CreateDirectory(cachePath);

                cache = gameObject.AddComponent <PersistentBlobCache>();
                cache.Setup(cachePath, config.maxCacheEntries, config.maxCacheSizeMb * 1024 * 1024);
            }
        }
Example #4
0
        private bool PrepareDownload(PolyAsset asset, out string baseName, out string downloadLocalPath)
        {
            assetsBeingDownloaded.Remove(asset);
            PtDebug.LogFormat("ABM: Preparing to download {0}", asset);

            // basePath is something like Assets/Poly/Sources.
            string baseLocalPath = PtUtils.NormalizeLocalPath(PtSettings.Instance.assetSourcesPath);

            if (!baseLocalPath.StartsWith("Assets/"))
            {
                Debug.LogErrorFormat("Invalid asset sources folder {0}. Must be under Assets folder.");
                baseName = downloadLocalPath = null;
                return(false);
            }

            // basePathAbs is something like C:\Users\foo\bar\MyUnityProject\Assets\Poly\Sources
            string baseFullPath = PtUtils.ToAbsolutePath(baseLocalPath);

            if (!Directory.Exists(baseFullPath))
            {
                Directory.CreateDirectory(baseFullPath);
            }

            baseName = PtUtils.GetPtAssetBaseName(asset);
            PtDebug.LogFormat("Import name: {0}", baseName);

            // downloadLocalPath is something like Assets/Poly/Sources/assetTitle_assetId
            downloadLocalPath = baseLocalPath + "/" + baseName;
            string downloadFullPath = PtUtils.ToAbsolutePath(downloadLocalPath);

            if (Directory.Exists(downloadFullPath))
            {
                if (PtSettings.Instance.warnOnSourceOverwrite &&
                    !EditorUtility.DisplayDialog("Warning: Overwriting asset source folder",
                                                 string.Format("The asset source folder '{0}' will be deleted and created again. " +
                                                               "This should be safe *unless* you have manually made changes to its contents, " +
                                                               "in which case you will lose those changes.\n\n" +
                                                               "(You can silence this warning in Poly Toolkit settings)",
                                                               asset.displayName, downloadLocalPath), "OK", "Cancel"))
                {
                    return(false);
                }
                Directory.Delete(downloadFullPath, /* recursive */ true);
            }

            // Create the download folder.
            // Something like C:\Users\foo\bar\MyUnityProject\Assets\Poly\Sources\assetTitle_assetId
            Directory.CreateDirectory(downloadFullPath);
            return(true);
        }
Example #5
0
        /// <summary>
        /// Starts downloading and importing the given asset (in the background). When done, the asset will
        /// be saved to the user's Assets folder.
        /// </summary>
        /// <param name="asset">The asset to download and import.</param>
        /// <param name="ptAssetLocalPath">Path to the PtAsset that should be created (or replaced).</param>
        /// <param name="options">Import options.</param>
        public void StartDownloadAndImport(PolyAsset asset, string ptAssetLocalPath, EditTimeImportOptions options)
        {
            if (!assetsBeingDownloaded.Add(asset))
            {
                return;
            }
            PtDebug.LogFormat("ABM: starting to fetch asset {0} ({1}) -> {2}", asset.name, asset.displayName,
                              ptAssetLocalPath);

            // Prefer glTF1 to glTF2.
            // It used to be that no Poly assets had both formats, so the ordering did not matter.
            // Blocks assets now have both glTF1 and glTF2. PT does not understand the glTF2 version,
            // so the ordering matters a great deal.
            PolyFormat glTF2format = asset.GetFormatIfExists(PolyFormatType.GLTF_2);
            PolyFormat glTFformat  = asset.GetFormatIfExists(PolyFormatType.GLTF);

            PolyMainInternal.FetchProgressCallback progressCallback = (PolyAsset assetBeingFetched, float progress) => {
                EditorUtility.DisplayProgressBar(DOWNLOAD_PROGRESS_TITLE, DOWNLOAD_PROGRESS_TEXT, progress);
            };

            if (glTFformat != null)
            {
                EditorUtility.DisplayProgressBar(DOWNLOAD_PROGRESS_TITLE, DOWNLOAD_PROGRESS_TEXT, 0.0f);
                PolyMainInternal.Instance.FetchFormatFiles(asset, PolyFormatType.GLTF,
                                                           (PolyAsset resultAsset, PolyStatus status) => {
                    EditorUtility.ClearProgressBar();
                    OnFetchFinished(status, resultAsset, /*isGltf2*/ false, ptAssetLocalPath, options);
                }, progressCallback);
            }
            else if (glTF2format != null)
            {
                EditorUtility.DisplayProgressBar(DOWNLOAD_PROGRESS_TITLE, DOWNLOAD_PROGRESS_TEXT, 0.0f);
                PolyMainInternal.Instance.FetchFormatFiles(asset, PolyFormatType.GLTF_2,
                                                           (PolyAsset resultAsset, PolyStatus status) => {
                    EditorUtility.ClearProgressBar();
                    OnFetchFinished(status, resultAsset, /*isGltf2*/ true, ptAssetLocalPath, options);
                }, progressCallback);
            }
            else
            {
                Debug.LogError("Asset not in GLTF_2 or GLTF format. Can't import.");
                PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Unsupported format");
            }
        }
Example #6
0
        private void OnFetchFinished(PolyStatus status, PolyAsset asset, bool isGltf2,
                                     string ptAssetLocalPath, EditTimeImportOptions options)
        {
            if (!status.ok)
            {
                Debug.LogErrorFormat("Error fetching asset {0} ({1}): {2}", asset.name, asset.displayName, status);
                EditorUtility.DisplayDialog("Download Error",
                                            string.Format("*** Error downloading asset '{0}'. Try again later.", asset.displayName), "OK");
                PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Asset fetch failed");
                return;
            }

            string baseName, downloadLocalPath;

            if (!PrepareDownload(asset, out baseName, out downloadLocalPath))
            {
                return;
            }

            string absPath = PtUtils.ToAbsolutePath(downloadLocalPath);

            string extension = isGltf2 ? ".gltf2" : ".gltf";
            string fileName  = baseName + extension;

            // We have to place an import request so that PolyImporter does the right thing when it sees the new file.
            PolyImporter.AddImportRequest(new PolyImporter.ImportRequest(
                                              downloadLocalPath + "/" + fileName, ptAssetLocalPath, options, asset));

            // Now unpackage it. GltfProcessor will pick it up automatically.
            UnpackPackageToFolder(isGltf2 ? asset.GetFormatIfExists(PolyFormatType.GLTF_2) :
                                  asset.GetFormatIfExists(PolyFormatType.GLTF), absPath, fileName);

            PtDebug.LogFormat("ABM: Successfully downloaded {0} to {1}", asset, absPath);
            AssetDatabase.Refresh();
            if (null != refreshCallback)
            {
                refreshCallback();
            }
        }
Example #7
0
        /// <summary>
        /// Sends a hit to Google Analytics.
        /// </summary>
        /// <param name="fields">Key-value pairs that make up the properties of the hit. These
        /// are assumed to be in the appropriate Google Analytics format.</param>
        private void SendHit(Dictionary <string, string> fields)
        {
            StringBuilder sb = new StringBuilder(prefix);

            foreach (KeyValuePair <string, string> pair in fields)
            {
                sb.AppendFormat("&{0}={1}", UnityWebRequest.EscapeURL(pair.Key), UnityWebRequest.EscapeURL(pair.Value));
            }
            string payload = sb.ToString();

            try {
                UnityWebRequest request = new UnityWebRequest("https://www.google-analytics.com/collect", "POST");
                request.uploadHandler             = new UploadHandlerRaw(Encoding.UTF8.GetBytes(payload));
                request.uploadHandler.contentType = "application/x-www-form-urlencoded";
                UnityCompat.SendWebRequest(request);
                PtDebug.LogFormat("ANALYTICS: sent hit: {0}", payload);
            } catch (Exception ex) {
                // Reporting these as errors would be noisy and annoying. We don't want to do that -- maybe the user is
                // offline. Not being able to send analytics isn't a big deal. So only log this error if
                // PtDebug verbose logging is on.
                PtDebug.LogFormat("*** Error sending analytics: {0}", ex);
            }
        }
Example #8
0
        /// <summary>
        /// Executes the given import request, producing a PtAsset and a prefab.
        /// </summary>
        /// <param name="request">The request to perform.</param>
        private static void ExecuteImportRequest(ImportRequest request)
        {
            PtDebug.LogFormat("Executing import request: {0}", request);

            string gltfFullPath   = PtUtils.ToAbsolutePath(request.gltfLocalPath);
            string assetLocalPath = request.ptAssetLocalPath;
            string assetFullPath  = PtUtils.ToAbsolutePath(assetLocalPath);

            PtAsset assetToReplace = AssetDatabase.LoadAssetAtPath <PtAsset>(assetLocalPath);

            GameObject prefabToReplace = null;

            if (assetToReplace != null)
            {
                if (assetToReplace.assetPrefab == null)
                {
                    Debug.LogErrorFormat("Couldn't find prefab for asset {0}.", assetToReplace);
                    PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Prefab not found");
                    return;
                }
                prefabToReplace = assetToReplace.assetPrefab;
            }

            // Determine if file is glTF2 or glTF1.
            bool isGltf2 = Path.GetExtension(request.gltfLocalPath) == ".gltf2";

            // First, import the GLTF and build a GameObject from it.
            EditorUtility.DisplayProgressBar(PROGRESS_BAR_TITLE, PROGRESS_BAR_TEXT, 0.5f);
            // Use a SanitizedPath stream loader because any format file we have downloaded and saved to disk we
            // have replaced the original relative path string with the MD5 string hash. This custom stream loader
            // will always convert uris passed to it to this hash value, and read them from there.
            IUriLoader binLoader = new HashedPathBufferedStreamLoader(Path.GetDirectoryName(gltfFullPath));

            ImportGltf.GltfImportResult result = null;
            using (TextReader reader = new StreamReader(gltfFullPath)) {
                result = ImportGltf.Import(isGltf2 ? GltfSchemaVersion.GLTF2 : GltfSchemaVersion.GLTF1,
                                           reader, binLoader, request.options.baseOptions);
            }
            EditorUtility.ClearProgressBar();
            string baseName = PtUtils.GetPtAssetBaseName(request.polyAsset);

            result.root.name = baseName;

            // Create the asset (delete it first if it exists).
            if (File.Exists(assetFullPath))
            {
                AssetDatabase.DeleteAsset(assetLocalPath);

                // If we are replacing an existing asset, we should rename the replacement to the new name,
                // since the name reflects the identity of the asset. So if the user is importing the asset
                // dog_a381b3g to replace what was previously cat_v81938.asset, the replacement file should
                // be named dog_a381b3g.asset, not cat_v81938.asset.
                assetLocalPath = PtUtils.GetDefaultPtAssetPath(request.polyAsset);
                assetFullPath  = PtUtils.ToAbsolutePath(assetLocalPath);
            }
            Directory.CreateDirectory(Path.GetDirectoryName(assetFullPath));

            // Create the new PtAsset and fill it in.
            AssetDatabase.CreateAsset(ScriptableObject.CreateInstance <PtAsset>(), assetLocalPath);
            PtAsset newAsset = AssetDatabase.LoadAssetAtPath <PtAsset>(assetLocalPath);

            newAsset.name    = baseName;
            newAsset.title   = request.polyAsset.displayName ?? "";
            newAsset.author  = request.polyAsset.authorName ?? "";
            newAsset.license = request.polyAsset.license;
            newAsset.url     = request.polyAsset.Url;

            // Ensure the imported object has a PtAssetObject component which references the PtAsset.
            result.root.AddComponent <PtAssetObject>().asset = newAsset;

            // Add all the meshes to the PtAsset.
            SaveMeshes(result.meshes, newAsset);

            // If the asset has materials, save those to the PtAsset.
            if (result.materials != null)
            {
                SaveMaterials(result.materials, newAsset);
            }

            // If the asset has textures, save those to the PtAsset.
            if (result.textures != null)
            {
                SaveTextures(result.textures, newAsset);
            }

            // Reimport is required to ensure custom asset displays correctly.
            AssetDatabase.ImportAsset(assetLocalPath);

            GameObject newPrefab;

            if (prefabToReplace)
            {
                // Replace the existing prefab with our new object, without breaking prefab connections.
                newPrefab = PrefabUtility.ReplacePrefab(result.root, prefabToReplace, ReplacePrefabOptions.ReplaceNameBased);
                AssetDatabase.RenameAsset(AssetDatabase.GetAssetPath(newPrefab), baseName);
            }
            else
            {
                // Create a new prefab.
                // Prefab path is the same as the asset path but with the extension changed to '.prefab'.
                string prefabLocalPath = Regex.Replace(assetLocalPath, "\\.asset$", ".prefab");
                if (!prefabLocalPath.EndsWith(".prefab"))
                {
                    Debug.LogErrorFormat("Error: failed to compute prefab path for {0}", assetLocalPath);
                    PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Prefab path error");
                    return;
                }
                newPrefab = PrefabUtility.CreatePrefab(prefabLocalPath, result.root);
            }

            // Now ensure the asset points to the prefab.
            newAsset.assetPrefab = newPrefab;
            if (newAsset.assetPrefab == null)
            {
                Debug.LogErrorFormat("Could not get asset prefab reference for asset {0}", newAsset);
                PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_FAILED, "Prefab ref error");
            }

            GameObject.DestroyImmediate(result.root);

            AssetDatabase.Refresh();

            if (request.options.alsoInstantiate)
            {
                PrefabUtility.InstantiatePrefab(newPrefab);
            }

            PtDebug.LogFormat("GLTF import complete: {0}", request);

            PtAnalytics.SendEvent(PtAnalytics.Action.IMPORT_SUCCESSFUL, isGltf2 ? "GLTF2" : "GLTF1");

            // If this is a third-party asset, we need to update the attributions file.
            AttributionFileGenerator.Generate(/* showUi */ false);

            EditorWindow.GetWindow <AssetBrowserWindow>().HandleAssetImported(request.polyAsset.name);

            // Select the prefab in the editor so the user knows where it is.
            AssetDatabase.Refresh();
            Selection.activeObject = newPrefab;
            EditorGUIUtility.PingObject(newPrefab);
        }