Пример #1
0
        /// <summary>
        /// Processes the result of fetching an individual file.
        /// </summary>
        /// <param name="state">Indicates the state of the ongoing fetch operation (as set up in FetchObj).</param>
        /// <param name="index">If ROOT_FILE_INDEX, then this is a result for the main file; else this is a result for
        /// the resource file with that index.</param>
        /// <param name="status">The status indicating if the download succeed</param>
        /// <param name="data">The data that was downloaded.</param>
        private void ProcessFileFetchResult(FetchOperationState state, int index, PolyStatus status, byte[] data)
        {
            if (state.pendingFiles == 0)
            {
                // Another request for this format failed, so we ignore any further responses.
                return;
            }

            if (!status.ok)
            {
                // This request failed, so we set pendingFiles to 0 so we ignore any further responses, and callback with
                // an error message.
                state.pendingFiles = 0;
                state.completionCallback(state.asset, PolyStatus.Error(status, "Failed to fetch file #{0}", index));
                return;
            }

            PolyFormat package = state.packageBeingFetched;
            PolyFile   file    = index == ROOT_FILE_INDEX ? package.root : package.resources[index];

            file.contents = data;

            --state.pendingFiles;
            if (state.progressCallback != null)
            {
                state.progressCallback(state.asset, 1.0f - ((float)state.pendingFiles / state.totalFiles));
            }
            if (state.pendingFiles <= 0)
            {
                // All files done, call callback indicating success.
                state.completionCallback(state.asset, PolyStatus.Success());
            }
        }
Пример #2
0
        /// <summary>
        /// Imports the given format of the given asset, asynchronously in a background thread.
        /// Calls the supplied callback when done.
        /// </summary>
        public void ImportAsync(PolyAsset asset, PolyFormat format, PolyImportOptions options,
                                AsyncImportCallback callback = null)
        {
            ImportOperation operation = new ImportOperation();

            operation.instance = this;
            operation.asset    = asset;
            operation.format   = format;
            operation.options  = options;
            operation.callback = callback;
            operation.status   = PolyStatus.Success();
            operation.loader   = new FormatLoader(format);
            if (Application.isPlaying)
            {
                Task.Run(() => BackgroundThreadProc(operation));
                //ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc), operation);
            }
            else
            {
                // If we are in the editor, don't do this in a background thread. Do it directly
                // here on the main thread.
                BackgroundThreadProc(operation);
                Update();
            }
        }
Пример #3
0
 /// <summary>
 /// Attempts to authenticate using the provided tokens.
 /// This will NOT launch a sign-in flow. It will use the given tokens directly.
 /// </summary>
 /// <param name="accessToken">The access token to use.</param>
 /// <param name="refreshToken">The refresh token to use.</param>
 /// <param name="callback">The callback to call when authentication completes.</param>
 public void Authenticate(string accessToken, string refreshToken, Action <PolyStatus> callback)
 {
     if (!instance.IsAuthenticationSupported)
     {
         callback(PolyStatus.Error("Authentication is not supported on this platform."));
     }
     oauth2Identity.LoginWithTokens(
         () => { callback(PolyStatus.Success()); },
         () => { callback(PolyStatus.Error("Authentication failed (with tokens).")); },
         accessToken, refreshToken);
 }
Пример #4
0
 /// <summary>
 /// Attempts to authenticate.
 /// </summary>
 /// <param name="interactive">If true, launch the sign in flow (browser) if necessary. If false,
 /// attempt to authenticate silently.</param>
 /// <param name="callback">Callback to call when authentication completes.</param>
 public void Authenticate(bool interactive, Action <PolyStatus> callback)
 {
     if (!instance.IsAuthenticationSupported)
     {
         callback(PolyStatus.Error("Authentication is not supported on this platform."));
     }
     oauth2Identity.Login(
         () => { callback(PolyStatus.Success()); },
         () => { callback(PolyStatus.Error("Authentication failed.")); },
         interactive);
 }
Пример #5
0
        /// <summary>
        ///   Refreshes an access token, if a given refresh token is valid, and then calls one of the given callbacks.
        /// </summary>
        public void Reauthorize(Action <PolyStatus> callback)
        {
            if (!instance.IsAuthenticationSupported)
            {
                callback(PolyStatus.Error("Authentication is not supported on this platform."));
            }

            CoroutineRunner.StartCoroutine(this, oauth2Identity.Reauthorize(
                                               successCallback: () => { callback(PolyStatus.Success()); },
                                               failureCallback: (string error) => { callback(PolyStatus.Error(error)); }
                                               ));
        }
Пример #6
0
        /// <summary>
        ///   Takes a string, representing either a ListAssetsResponse or ListUserAssetsResponse proto, and
        ///   fills polyListResult with relevant fields from the response and returns a success status
        ///   if the response is of the expected format, or a failure status if it's not.
        /// </summary>
        public static PolyStatus ParseReturnedAssets(string response, out PolyListAssetsResult polyListAssetsResult)
        {
            // Try and actually parse the string.
            JObject results = JObject.Parse(response);
            IJEnumerable <JToken> assets = results["assets"].AsJEnumerable();

            // If assets is null, check for a userAssets object, which would be present if the response was
            // a ListUserAssets response.
            if (assets == null)
            {
                assets = results["userAssets"].AsJEnumerable();
            }
            if (assets == null)
            {
                // Empty response means there were no assets that matched the request parameters.
                polyListAssetsResult = new PolyListAssetsResult(PolyStatus.Success(), /*totalSize*/ 0);
                return(PolyStatus.Success());
            }
            List <PolyAsset> polyAssets = new List <PolyAsset>();

            foreach (JToken asset in assets)
            {
                PolyAsset polyAsset;
                if (!(asset is JObject))
                {
                    Debug.LogWarningFormat("Ignoring asset since it's not a JSON object: " + asset);
                    continue;
                }
                JObject jObjectAsset = (JObject)asset;
                if (asset["asset"] != null)
                {
                    // If this isn't null, means we are parsing a ListUserAssets response, which has an added
                    // layer of nesting.
                    jObjectAsset = (JObject)asset["asset"];
                }
                PolyStatus parseStatus = ParseAsset(jObjectAsset, out polyAsset);
                if (parseStatus.ok)
                {
                    polyAssets.Add(polyAsset);
                }
                else
                {
                    Debug.LogWarningFormat("Failed to parse a returned asset: {0}", parseStatus);
                }
            }
            var totalSize = results["totalSize"] != null?int.Parse(results["totalSize"].ToString()) : 0;

            var nextPageToken = results["nextPageToken"] != null ? results["nextPageToken"].ToString() : null;

            polyListAssetsResult = new PolyListAssetsResult(PolyStatus.Success(), totalSize, polyAssets, nextPageToken);
            return(PolyStatus.Success());
        }
Пример #7
0
        /// <summary>
        /// Parses a single asset.
        /// </summary>
        public static PolyStatus ParseAsset(JObject asset, out PolyAsset polyAsset)
        {
            polyAsset = new PolyAsset();

            if (asset["visibility"] == null)
            {
                return(PolyStatus.Error("Asset has no visibility set."));
            }

            polyAsset.name       = asset["name"].ToString();
            polyAsset.authorName = asset["authorName"].ToString();
            if (asset["thumbnail"] != null)
            {
                var thumbnailElements = asset["thumbnail"].ToObject <JObject>();
                //IJEnumerable<JToken> thumbnailElements = asset["thumbnail"].AsJEnumerable();
                polyAsset.thumbnail = new PolyFile(thumbnailElements["relativePath"].ToString(),
                                                   thumbnailElements["url"].ToString(), thumbnailElements["contentType"].ToString());
            }

            if (asset["formats"] == null)
            {
                Debug.LogError("No formats found");
            }
            else
            {
                foreach (var format in asset["formats"].ToObject <List <JObject> >())
                //foreach (JToken format in asset["formats"])
                {
                    PolyFormat newFormat = ParseAssetsPackage(format);
                    newFormat.formatType = ParsePolyFormatType(format["formatType"]);
                    if (newFormat.formatType == PolyFormatType.UNKNOWN)
                    {
                        PtDebug.Log("Did not recognize format type: " + format["formatType"].ToString());
                    }
                    polyAsset.formats.Add(newFormat);
                }
            }
            polyAsset.displayName = asset["displayName"].ToString();
            polyAsset.createTime  = DateTime.Parse(asset["createTime"].ToString());
            polyAsset.updateTime  = DateTime.Parse(asset["updateTime"].ToString());
            polyAsset.visibility  = ParsePolyVisibility(asset["visibility"]);
            polyAsset.license     = ParsePolyAssetLicense(asset["license"]);
            if (asset["isCurated"] != null)
            {
                polyAsset.isCurated = bool.Parse(asset["isCurated"].ToString());
            }
            return(PolyStatus.Success());
        }
 /// <summary>
 /// Gets raw file data from Poly given a data URL.
 /// </summary>
 /// <param name="dataUrl">Data URL to retrieve from.</param>
 /// <param name="accessToken">The access token to use for authentication.</param>
 /// <param name="callback">The callback to call when the download is complete.</param>
 /// <param name="maxCacheAgeMillis">Maximum age of the cached copy, in millis. See
 /// WebRequestManager for useful constants.</param>
 public static void GetRawFileBytes(string dataUrl, string accessToken,
                                    long maxCacheAgeMillis, GetRawFileDataBytesCallback callback)
 {
     PolyMainInternal.Instance.webRequestManager.EnqueueRequest(
         () => { return(MakeRawFileGetRequest(dataUrl, accessToken)); },
         (PolyStatus status, int responseCode, byte[] response) => {
         if (!status.ok)
         {
             callback(PolyStatus.Error(status, "Failed to get raw file bytes for {0}", dataUrl), null);
         }
         else
         {
             callback(PolyStatus.Success(), response);
         }
     }, maxCacheAgeMillis);
 }
Пример #9
0
        public void Update()
        {
            // We process at most one import result per frame, to avoid doing too much work
            // in the main thread.
            ImportOperation operation;

            lock (finishedOperationsLock)
            {
                if (finishedOperations.Count == 0)
                {
                    return;
                }
                operation = finishedOperations.Dequeue();
            }

            if (!operation.status.ok)
            {
                // Import failed.
                operation.callback(operation.status, root: null, meshCreator: null);
                return;
            }

            try
            {
                IEnumerable meshCreator;
                ImportGltf.GltfImportResult result = ImportGltf.EndImport(operation.importState,
                                                                          operation.loader, out meshCreator);

                if (!operation.options.clientThrottledMainThread)
                {
                    // If we're not in throttled mode, create all the meshes immediately by exhausting
                    // the meshCreator enumeration. Otherwise, it's the caller's responsibility to
                    // do this.
                    foreach (var unused in meshCreator) /* empty */ } {
                    meshCreator = null;
            }
            // Success.
            operation.callback(PolyStatus.Success(), result.root, meshCreator);
        }
        catch (Exception ex)
        {
            // Import failed.
            Debug.LogException(ex);
            operation.callback(PolyStatus.Error("Failed to convert import to Unity objects.", ex),
                               root: null, meshCreator: null);
        }
    }
Пример #10
0
        /// <summary>
        /// Verify if the response can be parsed as json and, if so, that it contains no error token.
        /// If either conditions are false return a PolyStatusError with relevant information.
        /// </summary>
        private PolyStatus CheckResponseForError(string response)
        {
            JObject results = new JObject();

            try {
                results = JObject.Parse(response);
            } catch (Exception ex) {
                return(PolyStatus.Error("Failed to parse Poly API response, encountered exception: {0}", ex.Message));
            }
            IJEnumerable <JToken> error = results["error"].AsJEnumerable();

            if (error == null)
            {
                return(PolyStatus.Success());
            }
            return(PolyStatus.Error("{0}: {1}", error["code"] != null ? error["code"].ToString() : "(no error code)",
                                    error["message"] != null ? error["message"].ToString() : "(no error message)"));
        }
Пример #11
0
 public static PolyStatus ParseResponse(byte[] response, out JObject result)
 {
     try {
         result = JObject.Parse(Encoding.UTF8.GetString(response));
         JToken errorToken = result["error"];
         if (errorToken != null)
         {
             IJEnumerable <JToken> error = errorToken.AsJEnumerable();
             return(PolyStatus.Error("{0}: {1}", error["code"] != null ? error["code"].ToString() : "(no error code)",
                                     error["message"] != null ? error["message"].ToString() : "(no error message)"));
         }
         else
         {
             return(PolyStatus.Success());
         }
     } catch (Exception ex) {
         result = null;
         return(PolyStatus.Error("Failed to parse Poly API response, encountered exception: {0}", ex.Message));
     }
 }
Пример #12
0
    // Searches directly by Poly URL.
    private void SearchByPolyUrl(string polyUrl, OnActorableSearchResult resultCallback, System.Action <bool> onComplete)
    {
        string[] parts   = polyUrl.Split('/');
        string   assetId = parts[parts.Length - 1];

        PolyApi.GetAsset(assetId, result =>
        {
            PolyListAssetsResult assetsResult;
            List <PolyAsset> assetList = new List <PolyAsset>();
            if (result.Ok)
            {
                // Successfully got the asset. This is good.
                // Is it acceptably licensed?
                if (result.Value.license == PolyAssetLicense.CREATIVE_COMMONS_BY)
                {
                    // Good license. We can use it.
                    assetList.Add(result.Value);
                    assetsResult = new PolyListAssetsResult(PolyStatus.Success(), 1, assetList);
                }
                else
                {
                    // Not CC-By. Can't use it.
                    Debug.LogError("This asset (" + assetId + ") is not licensed by CC-By. Try another asset.");
                    assetsResult = new PolyListAssetsResult(PolyStatus.Error("Asset " + assetId + " is not licensed as CC-By."), 0, assetList);
                }
            }
            else
            {
                // Failed to get the asset. This is bad.
                assetsResult = new PolyListAssetsResult(PolyStatus.Error("Failed to get asset " + assetId), 0, assetList);
            }
            PolySearchCallback(
                new PolyStatusOr <PolyListAssetsResult>(assetsResult), resultCallback, onComplete);
        });
        return;
    }
Пример #13
0
        /// <summary>
        /// Parses a single asset.
        /// </summary>
        // public static PolyStatus ParseAsset(JObject asset, out PolyAsset polyAsset)
        // {
        //     polyAsset = new PolyAsset();

        //     if (asset["visibility"] == null)
        //     {
        //         return PolyStatus.Error("Asset has no visibility set.");
        //     }

        //     polyAsset.name = asset["name"].ToString();
        //     polyAsset.authorName = asset["authorName"].ToString();
        //     if (asset["thumbnail"] != null)
        //     {
        //         IJEnumerable<JToken> thumbnailElements = asset["thumbnail"].AsJEnumerable();
        //         polyAsset.thumbnail = new PolyFile(thumbnailElements["relativePath"].ToString(),
        //           thumbnailElements["url"].ToString(), thumbnailElements["contentType"].ToString());
        //     }

        //     if (asset["formats"] == null)
        //     {
        //         Debug.LogError("No formats found");
        //     }
        //     else
        //     {
        //         foreach (JToken format in asset["formats"])
        //         {
        //             PolyFormat newFormat = ParseAssetsPackage(format);
        //             newFormat.formatType = ParsePolyFormatType(format["formatType"]);
        //             if (newFormat.formatType == PolyFormatType.UNKNOWN)
        //             {
        //                 PtDebug.Log("Did not recognize format type: " + format["formatType"].ToString());
        //             }
        //             polyAsset.formats.Add(newFormat);
        //         }
        //     }
        //     polyAsset.displayName = asset["displayName"].ToString();
        //     polyAsset.createTime = DateTime.Parse(asset["createTime"].ToString());
        //     polyAsset.updateTime = DateTime.Parse(asset["updateTime"].ToString());
        //     polyAsset.visibility = ParsePolyVisibility(asset["visibility"]);
        //     polyAsset.license = ParsePolyAssetLicense(asset["license"]);
        //     if (asset["isCurated"] != null)
        //     {
        //         polyAsset.isCurated = bool.Parse(asset["isCurated"].ToString());
        //     }
        //     return PolyStatus.Success();
        // }

        // private static PolyFormatType ParsePolyFormatType(JToken token)
        // {
        //     if (token == null) return PolyFormatType.UNKNOWN;
        //     string tokenValue = token.ToString();
        //     return tokenValue == "OBJ" ? PolyFormatType.OBJ :
        //       tokenValue == "GLTF2" ? PolyFormatType.GLTF_2 :
        //       tokenValue == "GLTF" ? PolyFormatType.GLTF :
        //       tokenValue == "TILT" ? PolyFormatType.TILT :
        //       PolyFormatType.UNKNOWN;
        // }

        // private static PolyVisibility ParsePolyVisibility(JToken token)
        // {
        //     if (token == null) return PolyVisibility.UNSPECIFIED;
        //     string tokenValue = token.ToString();
        //     return tokenValue == "PRIVATE" ? PolyVisibility.PRIVATE :
        //       tokenValue == "UNLISTED" ? PolyVisibility.UNLISTED :
        //       tokenValue == "PUBLIC" ? PolyVisibility.PUBLISHED :
        //       PolyVisibility.UNSPECIFIED;
        // }

        // private static PolyAssetLicense ParsePolyAssetLicense(JToken token)
        // {
        //     if (token == null) return PolyAssetLicense.UNKNOWN;
        //     string tokenValue = token.ToString();
        //     return tokenValue == "CREATIVE_COMMONS_BY" ? PolyAssetLicense.CREATIVE_COMMONS_BY :
        //         tokenValue == "ALL_RIGHTS_RESERVED" ? PolyAssetLicense.ALL_RIGHTS_RESERVED :
        //         PolyAssetLicense.UNKNOWN;
        // }

        // As above, accepting a string response (such that we can parse on a background thread).
        public static PolyStatus ParseAsset(string response, out PolyAsset objectStoreEntry,
                                            bool hackUrls)
        {
            objectStoreEntry = null;
            return(PolyStatus.Success());
        }
Пример #14
0
        /// <summary>
        /// Co-routine that services one PendingRequest. This method must be called with StartCoroutine.
        /// </summary>
        /// <param name="request">The request to service.</param>
        private IEnumerator HandleWebRequest(PendingRequest request, BufferHolder bufferHolder)
        {
            // NOTE: This method runs on the main thread, but never blocks -- the blocking part of the work is
            // done by yielding the UnityWebRequest, which releases the main thread for other tasks while we
            // are waiting for the web request to complete (by the miracle of coroutines).

            // Let the caller create the UnityWebRequest, configuring it as they want. The caller can set the URL,
            // method, headers, anything they want. The only thing they can't do is call Send(), as we're in charge
            // of doing that.
            UnityWebRequest webRequest = request.creationCallback();

            PtDebug.LogVerboseFormat("Web request: {0} {1}", webRequest.method, webRequest.url);

            bool cacheAllowed = cache != null && webRequest.method == "GET" && request.maxAgeMillis != CACHE_NONE;

            // Check the cache (if it's a GET request and cache is enabled).
            if (cacheAllowed)
            {
                bool   cacheHit      = false;
                byte[] cacheData     = null;
                bool   cacheReadDone = false;
                cache.RequestRead(webRequest.url, request.maxAgeMillis, (bool success, byte[] data) =>
                {
                    cacheHit      = success;
                    cacheData     = data;
                    cacheReadDone = true;
                });
                while (!cacheReadDone)
                {
                    yield return(null);
                }
                if (cacheHit)
                {
                    PtDebug.LogVerboseFormat("Web request CACHE HIT: {0}, response: {1} bytes",
                                             webRequest.url, cacheData.Length);
                    request.completionCallback(PolyStatus.Success(), /* responseCode */ 200, cacheData);

                    // Return the buffer to the pool for reuse.
                    CleanUpAfterWebRequest(bufferHolder);

                    yield break;
                }
                else
                {
                    PtDebug.LogVerboseFormat("Web request CACHE MISS: {0}.", webRequest.url);
                }
            }

            DownloadHandlerBuffer handler = new DownloadHandlerBuffer();

            webRequest.downloadHandler = handler;

            // We need to asset that we actually succeeded in setting the download handler, because this can fail
            // if, for example, the creation callback mistakenly called Send().
            PolyUtils.AssertTrue(webRequest.downloadHandler == handler,
                                 "Couldn't set download handler. It's either disposed of, or the creation callback mistakenly called Send().");

            // Start the web request. This will suspend this coroutine until the request is done.
            PtDebug.LogVerboseFormat("Sending web request: {0}", webRequest.url);
            yield return(UnityCompat.SendWebRequest(webRequest));

            // Request is finished. Call user-supplied callback.
            PtDebug.LogVerboseFormat("Web request finished: {0}, HTTP response code {1}, response: {2}",
                                     webRequest.url, webRequest.responseCode, webRequest.downloadHandler.text);
            PolyStatus status = UnityCompat.IsNetworkError(webRequest) ? PolyStatus.Error(webRequest.error) : PolyStatus.Success();

            request.completionCallback(status, (int)webRequest.responseCode, webRequest.downloadHandler.data);

            // Cache the result, if applicable.
            if (!UnityCompat.IsNetworkError(webRequest) && cacheAllowed)
            {
                byte[] data = webRequest.downloadHandler.data;
                if (data != null && data.Length > 0)
                {
                    byte[] copy = new byte[data.Length];
                    Buffer.BlockCopy(data, 0, copy, 0, data.Length);
                    cache.RequestWrite(webRequest.url, copy);
                }
            }

            // Clean up.
            webRequest.Dispose();
            CleanUpAfterWebRequest(bufferHolder);
        }