Esempio n. 1
0
        /// <summary>
        /// Download main texture into memory. Can be used right away to create Unity texture.
        /// </summary>
        public virtual AsyncWebRequest <byte[]> DownloadTextureBytesAsync(AvatarData avatar)
        {
            var r = AvatarDataRequestAsync(avatar.texture);

            r.State = AvatarSdkMgr.Str(Strings.DownloadingHeadTexture);
            return(r);
        }
        /// <summary>
        /// Download all avatar files, unzip and save to disk.
        /// </summary>
        /// <param name="connection">Connection session.</param>
        /// <param name="avatar">Avatar to download.</param>
        /// <param name="withHaircutPointClouds">If set to true, download all haircut point clouds too.</param>
        /// <param name="withBlendshapes">If set to true, download blendshapes too.</param>
        public AsyncRequest DownloadAndSaveAvatarModelAsync(AvatarData avatar, bool withHaircutPointClouds, bool withBlendshapes)
        {
            var request = new AsyncRequest <AvatarData>(AvatarSdkMgr.Str(Strings.DownloadingAvatar));

            AvatarSdkMgr.SpawnCoroutine(DownloadAndSaveAvatarModel(avatar, withHaircutPointClouds, withBlendshapes, request));
            return(request);
        }
Esempio n. 3
0
        /// <summary>
        /// Edit avatar name/description on the server.
        /// </summary>
        public virtual AsyncWebRequest EditAvatarAsync(AvatarData avatar, string name = null, string description = null)
        {
            var request = new AsyncWebRequest(AvatarSdkMgr.Str(Strings.EditingAvatar));

            byte[] requestBodyData = null;
            using (var requestBody = new MultipartBody()) {
                requestBody.WriteTextField("name", name);
                requestBody.WriteTextField("description", description);
                requestBody.WriteFooter();
                requestBodyData = requestBody.GetRequestBodyData();

                Func <UnityWebRequest> webRequestFactory = () => {
                    var webRequest = UnityWebRequest.Post(avatar.url, " ");
                    webRequest.chunkedTransfer = false;
                    webRequest.method          = "PATCH";
                    webRequest.uploadHandler   = new UploadHandlerRaw(requestBodyData);
                    webRequest.SetRequestHeader(
                        "Content-Type", string.Format("multipart/form-data; boundary=\"{0}\"", requestBody.Boundary)
                        );
                    SetAuthHeaders(webRequest);
                    return(webRequest);
                };

                Debug.LogFormat("Uploading photo...");
                AvatarSdkMgr.SpawnCoroutine(AwaitJsonWebRequest(webRequestFactory, request));
                return(request);
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Get list of all textures for avatar.
        /// </summary>
        public virtual AsyncWebRequest <TextureData[]> GetTexturesAsync(AvatarData avatar)
        {
            var r = AvatarJsonArrayRequest <TextureData> (GetUrl("avatars", avatar.code, "textures"));

            r.State = AvatarSdkMgr.Str(Strings.RequestingTextureInfo);
            return(r);
        }
Esempio n. 5
0
        /// <summary>
        /// Downloading coordinates of the vertices of the head model. This can be used to save download time, because faces and UV are always the same.
        /// </summary>
        public virtual AsyncWebRequest <byte[]> DownloadPointCloudZipAsync(AvatarData avatar)
        {
            var r = AvatarDataRequestAsync(GetUrl("avatars", avatar.code, "pointcloud"));

            r.State = AvatarSdkMgr.Str(Strings.DownloadingHeadMesh);
            return(r);
        }
Esempio n. 6
0
        /// <summary>
        /// Get list of all haircuts for avatar.
        /// </summary>
        public virtual AsyncWebRequest <AvatarHaircutData[]> GetHaircutsAsync(AvatarData avatar)
        {
            var r = AvatarJsonArrayRequest <AvatarHaircutData> (avatar.haircuts);

            r.State = AvatarSdkMgr.Str(Strings.RequestingHaircutInfo);
            return(r);
        }
Esempio n. 7
0
        /// <summary>
        /// Wait until avatar is calculated. Report progress through the async request object.
        /// This function will return error (request.IsError == true) only if calculations failed on server or
        /// avatar has been deleted from the server. In all other cases it will continue to poll status.
        /// </summary>
        public virtual AsyncRequest <AvatarData> AwaitAvatarCalculationsAsync(AvatarData avatar)
        {
            var request = new AsyncRequest <AvatarData> (AvatarSdkMgr.Str(Strings.StartingCalculations));

            AvatarSdkMgr.SpawnCoroutine(AwaitAvatarCalculationsLoop(avatar, request));
            return(request);
        }
Esempio n. 8
0
        /// <summary>
        /// Download mesh zip file into memory.
        /// </summary>
        /// <param name="levelOfDetails">Level of mesh details. 0 - highest resolution, 7 - lowest resolution</param>
        /// <returns></returns>
        public virtual AsyncWebRequest <byte[]> DownloadMeshZipAsync(AvatarData avatar, int levelOfDetails = 0)
        {
            var url = UrlWithParams(avatar.mesh, "lod", levelOfDetails.ToString());
            var r   = AvatarDataRequestAsync(url);

            r.State = AvatarSdkMgr.Str(Strings.DownloadingHeadMesh);
            return(r);
        }
Esempio n. 9
0
        /// <summary>
        /// Download all avatar files, unzip and save to disk.
        /// </summary>
        /// <param name="connection">Connection session.</param>
        /// <param name="avatar">Avatar to download.</param>
        /// <param name="withHaircutPointClouds">If set to true, download all haircut point clouds too.</param>
        /// <param name="withBlendshapes">If set to true, download blendshapes too.</param>
        public static AsyncRequest <AvatarData> DownloadAndSaveAvatarModelAsync(
            Connection connection, AvatarData avatar, bool withHaircutPointClouds, bool withBlendshapes
            )
        {
            Debug.LogWarning("This method is obsolete. Use CloudAvatarProvider instead.");
            var request = new AsyncRequest <AvatarData> (AvatarSdkMgr.Str(Strings.DownloadingAvatar));

            AvatarSdkMgr.SpawnCoroutine(DownloadAndSaveAvatarModel(connection, avatar, withHaircutPointClouds, withBlendshapes, request));
            return(request);
        }
Esempio n. 10
0
        /// <summary>
        /// Download thumbnail with the specified resolution.
        /// </summary>
        /// <param name="avatar">Avatar data</param>
        /// <returns></returns>
        public virtual AsyncWebRequest <byte[]> DownloadAvatarThumbnailBytesAsync(AvatarData avatar, int maxW, int maxH)
        {
            var param = new Dictionary <string, string> {
                { "max_w", maxW.ToString() },
                { "max_h", maxH.ToString() },
            };
            var url = UrlWithParams(avatar.thumbnail, param);

            var r = AvatarDataRequestAsync(url);

            r.State = AvatarSdkMgr.Str(Strings.DownloadingThumbnail);
            return(r);
        }
Esempio n. 11
0
        /// <summary>
        /// Delete avatar record on the server (does not delete local files).
        /// </summary>
        public virtual AsyncWebRequest DeleteAvatarAsync(AvatarData avatar)
        {
            var request = new AsyncWebRequest(AvatarSdkMgr.Str(Strings.DeletingAvatarOnServer));

            Func <UnityWebRequest> webRequestFactory = () => {
                var webRequest = UnityWebRequest.Delete(avatar.url);
                SetAuthHeaders(webRequest);
                webRequest.downloadHandler = new DownloadHandlerBuffer();
                return(webRequest);
            };

            AvatarSdkMgr.SpawnCoroutine(AwaitWebRequest(webRequestFactory, request));
            return(request);
        }
Esempio n. 12
0
        /// <summary>
        /// Downloads zip archive with all the blendshapes.
        /// </summary>
        /// <param name="format">Format of blendshapes inside the zip file. Use BIN if you don't know which one to choose.</param>
        /// <param name="levelOfDetails">Level of mesh details. 0 - highest resolution, 7 - lowest resolution</param>
        public virtual AsyncWebRequest <byte[]> DownloadBlendshapesZipAsync(AvatarData avatar, BlendshapesFormat format = BlendshapesFormat.BIN, int levelOfDetails = 0)
        {
            var fmtToStr = new Dictionary <BlendshapesFormat, string> {
                { BlendshapesFormat.BIN, "bin" },
                { BlendshapesFormat.FBX, "fbx" },
                { BlendshapesFormat.PLY, "ply" },
            };

            string url = string.Format("{0}?fmt={1}&lod={2}", avatar.blendshapes, fmtToStr [format], levelOfDetails);
            var    r   = AvatarDataRequestAsync(url);

            r.State = AvatarSdkMgr.Str(Strings.DownloadingBlendshapes);
            return(r);
        }
Esempio n. 13
0
        /// <summary>
        /// AwaitAvatarCalculationsAsync implementation.
        /// </summary>
        private IEnumerator AwaitAvatarCalculationsLoop(AvatarData avatar, AsyncRequest <AvatarData> request)
        {
            while (!Strings.FinalStates.Contains(avatar.status))
            {
                yield return(new WaitForSeconds(4));

                var avatarStatusRequest = GetAvatarAsync(avatar.code);
                yield return(avatarStatusRequest);

                if (avatarStatusRequest.Status.Value == (long)StatusCode.Code.NOT_FOUND)
                {
                    Debug.LogWarning("404 error most likely means that avatar was deleted from the server");
                    request.SetError(string.Format("Avatar status response: {0}", avatarStatusRequest.ErrorMessage));
                    yield break;
                }

                if (avatarStatusRequest.Status.Value == (long)StatusCode.Code.TOO_MANY_REQUESTS_THROTTLING)
                {
                    Debug.LogWarning("Too many requests!");
                    yield return(new WaitForSeconds(10));
                }

                if (avatarStatusRequest.IsError)
                {
                    Debug.LogWarningFormat("Status polling error: {0}", avatarStatusRequest.ErrorMessage);
                    // Most likely this is a temporary issue. Keep polling.
                    continue;
                }

                avatar = avatarStatusRequest.Result;
                Debug.LogFormat("Status: {0}, progress: {1}%", avatar.status, avatar.progress);
                request.State = AvatarSdkMgr.Str(avatar.status);

                if (avatar.status == Strings.Computing)
                {
                    request.Progress = (float)avatar.progress / 100;
                }
            }

            if (Strings.GoodFinalStates.Contains(avatar.status))
            {
                request.Result = avatar;
                request.IsDone = true;
            }
            else
            {
                request.SetError(string.Format("Avatar calculations failed, status: {0}", avatar.status));
            }
        }
Esempio n. 14
0
        /// <summary>
        /// GenerateAndSaveAvatarAsync implementation.
        /// </summary>
        private static IEnumerator GenerateAndSaveAvatarFunc(
            Connection connection,
            string name,
            string description,
            byte[] photoBytes,
            bool withHaircutPointClouds,
            bool withBlendshapes,
            bool forcePowerOfTwoTexture,
            AsyncRequest <AvatarData> request
            )
        {
            // uploading photo and registering new avatar on the server
            var createAvatar = connection.CreateAvatarWithPhotoAsync(name, description, photoBytes, forcePowerOfTwoTexture);

            // Wait until async request is completed (without blocking the main thread).
            // Instead of using AwaitSubrequest we could just use `yield return createAvatar;`
            // AwaitSubrequest is a helper function that allows to track progress on composite
            // requests automatically. It also provides info for the caller about current subrequest
            // (and it's progress) and propagetes error from subrequest to the parent request.
            // finalProgress is a value between 0 and 1, a desired progress of parent request when given
            // subrequest is completed.
            yield return(request.AwaitSubrequest(createAvatar, finalProgress: 0.19f));

            // must check whether request was successful before proceeding
            if (request.IsError)
            {
                yield break;
            }

            // Result field contains, well, result of the request. In this case it's an AvatarData object.
            AvatarData avatar = createAvatar.Result;

            // save photo for later use
            var savePhoto = CoreTools.SaveAvatarFileAsync(photoBytes, avatar.code, AvatarFile.PHOTO);

            yield return(request.AwaitSubrequest(savePhoto, finalProgress: 0.2f));

            // again, must check for the error, there's no point in proceeding otherwise
            if (request.IsError)
            {
                yield break;
            }

            // Server starts calculating 3D shape and texture after photo has been uploaded.
            // Now we must wait until calculations finish.
            var awaitCalculations = connection.AwaitAvatarCalculationsAsync(avatar);

            yield return(request.AwaitSubrequest(awaitCalculations, finalProgress: 0.95f));

            if (request.IsError)
            {
                yield break;
            }

            // calculations finished, update avatar info from the latest result
            avatar = awaitCalculations.Result;

            // download, save and unzip all files
            var downloadAndSave = DownloadAndSaveAvatarModelAsync(connection, avatar, withHaircutPointClouds, withBlendshapes);

            yield return(request.AwaitSubrequest(downloadAndSave, finalProgress: 1));

            if (request.IsError)
            {
                yield break;
            }

            // At this point we have all avatar files stored in the filesystem ready for being loaded and displayed.
            // Our job is considered done here.
            request.Result = avatar;
            request.IsDone = true;
        }
Esempio n. 15
0
        /// <summary>
        /// DownloadAndSaveAvatarModelAsync implementation.
        /// </summary>
        private static IEnumerator DownloadAndSaveAvatarModel(
            Connection connection,
            AvatarData avatar,
            bool withHaircutPointClouds,
            bool withBlendshapes,
            AsyncRequest <AvatarData> request
            )
        {
            // By initializing multiple requests at the same time (without yield between them) we're
            // starting them in parallel. In this particular case we're downloading multiple files at the same time,
            // which is usually a bit faster than sequential download.
            var meshZip        = connection.DownloadMeshZipAsync(avatar);
            var textureRequest = connection.DownloadTextureBytesAsync(avatar);

            var download = new List <AsyncRequest> {
                meshZip, textureRequest
            };
            AsyncWebRequest <byte[]> allHaircutPointCloudsZip = null, blendshapesZip = null;

                        #if BLENDSHAPES_IN_PLY_OR_FBX
            // just a sample of how to get blendshapes in a different format

            AsyncWebRequest <byte[]> blendshapesZipFbx = null, blendshapesZipPly = null;
                        #endif

            if (withHaircutPointClouds)
            {
                allHaircutPointCloudsZip = connection.DownloadAllHaircutPointCloudsZipAsync(avatar);
                download.Add(allHaircutPointCloudsZip);
            }

            if (withBlendshapes)
            {
                blendshapesZip = connection.DownloadBlendshapesZipAsync(avatar);
                download.Add(blendshapesZip);

                                #if BLENDSHAPES_IN_PLY_OR_FBX
                // just a sample of how to get blendshapes in a different format

                blendshapesZipFbx = connection.DownloadBlendshapesZipAsync(avatar, BlendshapesFormat.FBX);
                download.Add(blendshapesZipFbx);

                blendshapesZipPly = connection.DownloadBlendshapesZipAsync(avatar, BlendshapesFormat.PLY);
                download.Add(blendshapesZipPly);
                                #endif
            }

            // continue execution when all requests finish
            yield return(request.AwaitSubrequests(0.9f, download.ToArray()));

            // return if any of the requests failed
            if (request.IsError)
            {
                yield break;
            }

            // save all the results to disk, also in parallel
            var saveMeshZip = CoreTools.SaveAvatarFileAsync(meshZip.Result, avatar.code, AvatarFile.MESH_ZIP);
            var saveTexture = CoreTools.SaveAvatarFileAsync(textureRequest.Result, avatar.code, AvatarFile.TEXTURE);

            var save = new List <AsyncRequest> ()
            {
                saveMeshZip, saveTexture
            };
            AsyncRequest <string> saveHaircutPointsZip = null, saveBlendshapesZip = null;
            if (allHaircutPointCloudsZip != null)
            {
                saveHaircutPointsZip = CoreTools.SaveAvatarFileAsync(allHaircutPointCloudsZip.Result, avatar.code, AvatarFile.ALL_HAIRCUT_POINTS_ZIP);
                save.Add(saveHaircutPointsZip);
            }

            if (blendshapesZip != null)
            {
                saveBlendshapesZip = CoreTools.SaveAvatarFileAsync(blendshapesZip.Result, avatar.code, AvatarFile.BLENDSHAPES_ZIP);
                save.Add(saveBlendshapesZip);
            }

                        #if BLENDSHAPES_IN_PLY_OR_FBX
            // just a sample of how to get blendshapes in a different format

            if (blendshapesZipFbx != null)
            {
                var saveBlendshapesZipFbx = CoreTools.SaveAvatarFileAsync(blendshapesZipFbx.Result, avatar.code, AvatarFile.BLENDSHAPES_FBX_ZIP);
                save.Add(saveBlendshapesZipFbx);
            }

            if (blendshapesZipPly != null)
            {
                var saveBlendshapesZipPly = CoreTools.SaveAvatarFileAsync(blendshapesZipPly.Result, avatar.code, AvatarFile.BLENDSHAPES_PLY_ZIP);
                save.Add(saveBlendshapesZipPly);
            }
                        #endif

            yield return(request.AwaitSubrequests(0.95f, save.ToArray()));

            if (request.IsError)
            {
                yield break;
            }

            var unzipMesh = CoreTools.UnzipFileAsync(saveMeshZip.Result);

            var unzip = new List <AsyncRequest> ()
            {
                unzipMesh
            };
            AsyncRequest <string> unzipHaircutPoints = null, unzipBlendshapes = null;
            if (saveHaircutPointsZip != null)
            {
                unzipHaircutPoints = CoreTools.UnzipFileAsync(saveHaircutPointsZip.Result);
                unzip.Add(unzipHaircutPoints);
            }
            if (saveBlendshapesZip != null)
            {
                unzipBlendshapes = UnzipBlendshapes(saveBlendshapesZip.Result, avatar.code);
                unzip.Add(unzipBlendshapes);
            }

            yield return(request.AwaitSubrequests(0.99f, unzip.ToArray()));

            if (request.IsError)
            {
                yield break;
            }

            // delete all .zip files we don't need anymore
            try {
                foreach (var fileToDelete in new AvatarFile[] { AvatarFile.MESH_ZIP, AvatarFile.ALL_HAIRCUT_POINTS_ZIP, AvatarFile.BLENDSHAPES_ZIP })
                {
                    CoreTools.DeleteAvatarFile(avatar.code, fileToDelete);
                }
            } catch (Exception ex) {
                // error here is not critical, we can just ignore it
                Debug.LogException(ex);
            }

            request.Result = avatar;
            request.IsDone = true;
        }
Esempio n. 16
0
        /// <summary>
        /// Downloads zip archive with point clouds for all haircuts. It is recommended to use this request
        /// for less overall download time (instead of downloading all individual haircuts separately).
        /// </summary>
        public virtual AsyncWebRequest <byte[]> DownloadAllHaircutPointCloudsZipAsync(AvatarData avatar)
        {
            string url = string.Format("{0}pointclouds/", avatar.haircuts);
            var    r   = AvatarDataRequestAsync(url);

            r.State = AvatarSdkMgr.Str(Strings.DownloadingAllHaircutPointClouds);
            return(r);
        }