/// <summary>
        /// GetHaircutPreviewAsync implementation
        /// </summary>
        private IEnumerator GetHaircutPreviewFunc(string avatarCode, string haircutId, AsyncRequest <byte[]> request)
        {
            string haircutPreviewFilename = AvatarSdkMgr.Storage().GetHaircutFilename(haircutId, HaircutFile.HAIRCUT_PREVIEW);

            if (!File.Exists(haircutPreviewFilename))
            {
                var haircutDataRequest = GetHaircutDataAsync(avatarCode, haircutId);
                yield return(request.AwaitSubrequest(haircutDataRequest, 0.05f));

                if (request.IsError)
                {
                    yield break;
                }

                var downloadRequest = DownloadAndSaveHaircutPreviewAsync(haircutDataRequest.Result);

                yield return(request.AwaitSubrequest(downloadRequest, 0.9f));

                if (request.IsError)
                {
                    yield break;
                }
            }

            byte[] previewBytes = File.ReadAllBytes(haircutPreviewFilename);

            request.IsDone = true;
            request.Result = previewBytes;
        }
        /// <summary>
        /// DownloadAndSaveHaircutPointsAsync implementation
        /// </summary>
        private IEnumerator DownloadAndSaveHaircutPointsFunc(string avatarCode, AvatarHaircutData haircutData, AsyncRequest request)
        {
            var haircutPointsRequest = connection.DownloadHaircutPointCloudZipAsync(haircutData);

            yield return(request.AwaitSubrequest(haircutPointsRequest, 0.9f));

            if (request.IsError)
            {
                yield break;
            }

            var saveHaircutPointsRequest = CoreTools.SaveAvatarHaircutPointCloudZipFileAsync(haircutPointsRequest.Result, avatarCode, haircutData.identity);

            yield return(request.AwaitSubrequest(saveHaircutPointsRequest, 0.95f));

            if (request.IsError)
            {
                yield break;
            }

            var unzipPointsRequest = CoreTools.UnzipFileAsync(saveHaircutPointsRequest.Result);

            yield return(request.AwaitSubrequest(unzipPointsRequest, 1.0f));

            if (request.IsError)
            {
                yield break;
            }

            request.IsDone = true;
        }
        /// <summary>
        /// GetHaircutMeshAsync implementation
        /// </summary>
        private IEnumerator GetHaircutMeshFunc(string avatarCode, string haircutId, AsyncRequest <TexturedMesh> request)
        {
            DateTime startTime = DateTime.Now;
            // In order to display the haircut in a scene correctly we need three things: mesh, texture, and coordinates of
            // vertices adjusted specifically for our avatar (this is called "haircut point cloud"). We need this because
            // algorithms automatically adjust haircuts for each model to provide better fitness.
            // Haircut texture and mesh (number of points and mesh topology) are equal for all avatars, but "point cloud"
            // should be downloaded separately for each model.
            // If mesh and texture are not cached yet, lets download and save them.
            string haircutMeshFilename       = AvatarSdkMgr.Storage().GetHaircutFilename(haircutId, HaircutFile.HAIRCUT_MESH_PLY);
            string haircutTextureFilename    = AvatarSdkMgr.Storage().GetHaircutFilename(haircutId, HaircutFile.HAIRCUT_TEXTURE);
            string haircutPointCloudFilename = AvatarSdkMgr.Storage().GetAvatarHaircutPointCloudFilename(avatarCode, haircutId);

            bool existMeshFiles  = File.Exists(haircutMeshFilename) && File.Exists(haircutTextureFilename);
            bool existPointcloud = File.Exists(haircutPointCloudFilename);

            if (!existMeshFiles || !existPointcloud)
            {
                var haircutDataRequest = GetHaircutDataAsync(avatarCode, haircutId);
                yield return(request.AwaitSubrequest(haircutDataRequest, 0.05f));

                if (request.IsError)
                {
                    yield break;
                }

                List <AsyncRequest> downloadRequests = new List <AsyncRequest>();
                if (!existMeshFiles)
                {
                    downloadRequests.Add(DownloadAndSaveHaircutMeshAsync(haircutDataRequest.Result));
                }
                if (!existPointcloud)
                {
                    downloadRequests.Add(DownloadAndSaveHaircutPointsAsync(avatarCode, haircutDataRequest.Result));
                }

                yield return(request.AwaitSubrequests(0.9f, downloadRequests.ToArray()));

                if (request.IsError)
                {
                    yield break;
                }
            }

            var loadHaircutRequest = CoreTools.LoadHaircutFromDiskAsync(avatarCode, haircutId);

            yield return(request.AwaitSubrequest(loadHaircutRequest, 1.0f));

            if (request.IsError)
            {
                yield break;
            }

            request.IsDone = true;
            request.Result = loadHaircutRequest.Result;
        }
Exemple #4
0
        /// <summary>
        /// AuthorizeAsync implementation.
        /// </summary>
        private IEnumerator Authorize(AsyncRequest request)
        {
            var accessCredentials = AuthUtils.LoadCredentials();

            if (accessCredentials == null || string.IsNullOrEmpty(accessCredentials.clientSecret))
            {
                request.SetError("Could not find API keys! Please provide valid credentials via Window->ItSeez3D Avatar SDK");
                yield break;
            }

            var authRequest = AuthorizeClientCredentialsGrantTypeAsync(accessCredentials);

            yield return(request.AwaitSubrequest(authRequest, 0.5f));

            if (request.IsError)
            {
                yield break;
            }

            tokenType   = authRequest.Result.token_type;
            accessToken = authRequest.Result.access_token;
            Debug.LogFormat("Successful authentication!");

            // guarantees we re-register a Player if clientId changes
            var playerIdentifier = string.Format("player_uid_{0}", accessCredentials.clientId.Substring(0, accessCredentials.clientId.Length / 3));

            if (string.IsNullOrEmpty(playerUID))
            {
                playerUID = AvatarSdkMgr.Storage().LoadPlayerUID(playerIdentifier);
            }

            if (string.IsNullOrEmpty(playerUID))
            {
                Debug.Log("Registering new player UID");
                var playerRequest = RegisterPlayerAsync();
                yield return(request.AwaitSubrequest(playerRequest, 1));

                if (request.IsError)
                {
                    yield break;
                }

                playerUID = playerRequest.Result.code;
                AvatarSdkMgr.Storage().StorePlayerUID(playerIdentifier, playerUID);
            }

            request.IsDone = true;
        }
        /// <summary>
        /// DeleteAvatarAsync implementation
        /// </summary>
        private IEnumerator DeleteAvatarFunc(string avatarCode, AsyncRequest request)
        {
            var avatarRequest = GetAvatarAsync(avatarCode);

            yield return(avatarRequest);

            if (avatarRequest.IsError)
            {
                request.SetError(avatarRequest.ErrorMessage);
                yield break;
            }

            var deleteRequest = connection.DeleteAvatarAsync(avatarRequest.Result);

            yield return(request.AwaitSubrequest(deleteRequest, 0.5f));

            if (request.IsError)
            {
                yield break;
            }

            CoreTools.DeleteAvatarFiles(avatarCode);

            request.IsDone = true;
        }
        /// <summary>
        /// StartAndAwaitAvatarCalculationAsync implementation
        /// </summary>
        private IEnumerator StartAndAwaitAvatarCalculationFunc(string avatarCode, AsyncRequest request)
        {
            var avatarRequest = GetAvatarAsync(avatarCode);

            yield return(avatarRequest.Await());

            if (avatarRequest.IsError)
            {
                request.SetError(avatarRequest.ErrorMessage);
                yield break;
            }

            var awaitCalculations = connection.AwaitAvatarCalculationsAsync(avatarRequest.Result);

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

            if (request.IsError)
            {
                yield break;
            }

            if (Strings.BadFinalStates.Contains(awaitCalculations.Result.status))
            {
                request.SetError(string.Format("Avatar {0} calculation finished with status: {1}", awaitCalculations.Result.code, awaitCalculations.Result.status));
                yield break;
            }

            request.IsDone = true;
        }
        /// <summary>
        /// DownloadAndSaveHaircutPreviewAsync implementation
        /// </summary>
        private IEnumerator DownloadAndSaveHaircutPreviewFunc(AvatarHaircutData haircutData, AsyncRequest request)
        {
            Debug.LogFormat("Downloading haircut preview...");
            var haircutPreviewRequest = connection.DownloadHaircutPreviewBytesAsync(haircutData);

            yield return(request.AwaitSubrequest(haircutPreviewRequest, 0.8f));

            if (request.IsError)
            {
                yield break;
            }

            Debug.LogFormat("Saving haircut preview to disk...");
            var saveHaircutPreviewRequest = CoreTools.SaveHaircutFileAsync(haircutPreviewRequest.Result, haircutData.identity, HaircutFile.HAIRCUT_PREVIEW);

            yield return(request.AwaitSubrequest(saveHaircutPreviewRequest, 0.9f));

            if (request.IsError)
            {
                yield break;
            }

            request.IsDone = true;
        }
        /// <summary>
        /// GetHaircutDataAsync implementation
        /// </summary>
        private IEnumerator GetHaircutDataFunc(string avatarCode, string haircutId, AsyncRequest <AvatarHaircutData> request)
        {
            bool takeFromCache = UseCache && haircutsDataCache.ContainsKey(avatarCode);

            if (takeFromCache)
            {
                request.Result = haircutsDataCache[avatarCode].FirstOrDefault(h => string.Compare(h.identity, haircutId) == 0);
            }
            else
            {
                // get AvatarData firstly.
                // If you would like to make multiple requests for getting haircut data, it is better to get AvatarData only once and store it somewhere
                var avatarRequest = GetAvatarAsync(avatarCode);
                yield return(avatarRequest.Await());

                if (avatarRequest.IsError)
                {
                    request.SetError(avatarRequest.ErrorMessage);
                    yield break;
                }

                var haircutInfoRequest = connection.GetHaircutsAsync(avatarRequest.Result);
                yield return(request.AwaitSubrequest(haircutInfoRequest, 0.9f));

                if (request.IsError)
                {
                    yield break;
                }

                if (UseCache)
                {
                    haircutsDataCache.Add(avatarCode, haircutInfoRequest.Result);
                }

                AvatarHaircutData haircutData = haircutInfoRequest.Result.FirstOrDefault(h => string.Compare(h.identity, haircutId) == 0);
                if (haircutData == null)
                {
                    Debug.LogErrorFormat("There is no {0} haircut for avatar with code: {1}", haircutId, avatarCode);
                    yield break;
                }
                request.Result = haircutData;
            }

            request.IsDone = true;
        }
        /// <summary>
        /// GetHaircutsIdAsync implementation
        /// </summary>
        private IEnumerator GetHaircutsIdFunc(string avatarCode, AsyncRequest <string[]> request)
        {
            bool takeFromCache = UseCache && haircutsDataCache.ContainsKey(avatarCode);

            if (takeFromCache)
            {
                request.Result = haircutsDataCache[avatarCode].Select(h => h.identity).ToArray();
            }
            else
            {
                var avatarRequest = GetAvatarAsync(avatarCode);
                yield return(avatarRequest.Await());

                if (avatarRequest.IsError)
                {
                    request.SetError(avatarRequest.ErrorMessage);
                    yield break;
                }

                if (supportedHaircutsPipelines.Contains(avatarRequest.Result.pipeline))
                {
                    var haircutInfoRequest = connection.GetHaircutsAsync(avatarRequest.Result);
                    yield return(request.AwaitSubrequest(haircutInfoRequest, 0.9f));

                    if (request.IsError)
                    {
                        yield break;
                    }

                    request.Result = haircutInfoRequest.Result.Select(h => h.identity).ToArray();

                    if (UseCache)
                    {
                        haircutsDataCache.Add(avatarCode, haircutInfoRequest.Result);
                    }
                }
                else
                {
                    Debug.LogFormat("{0} doesn't support haircuts", avatarRequest.Result.pipeline);
                }
            }

            request.IsDone = true;
        }
        /// <summary>
        /// InitializeAvatarAsync implementation
        /// </summary>
        private IEnumerator InitializeAvatarFunc(byte[] photoBytes, string name, string description, PipelineType pipeline,
                                                 AvatarResources resources, AsyncRequest <string> request)
        {
            // uploading photo and registering new avatar on the server
            var createAvatar = connection.CreateAvatarWithPhotoAsync(name, description, photoBytes, false, pipeline, resources);

            // 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.99f));

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

            string avatarCode = createAvatar.Result.code;

            // save photo for later use
            var savePhoto = CoreTools.SaveAvatarFileAsync(photoBytes, avatarCode, AvatarFile.PHOTO);
            // save pipeline type
            var savePipeline = CoreTools.SaveAvatarFileAsync(Encoding.ASCII.GetBytes(pipeline.GetPipelineTypeName()), avatarCode, AvatarFile.PIPELINE_INFO);

            yield return(request.AwaitSubrequests(1.0f, savePhoto, savePipeline));

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

            request.Result = avatarCode;
            request.IsDone = true;
        }
        private IEnumerator GetHaircutsIdsFunc(string avatarCode, AsyncRequest <string[]> request)
        {
            string[] haircuts = null;
            if (cachedHaircuts.ContainsKey(avatarCode))
            {
                haircuts = cachedHaircuts[avatarCode];
            }
            else
            {
                var haircutsRequest = avatarProvider.GetHaircutsIdAsync(avatarCode);
                yield return(request.AwaitSubrequest(haircutsRequest, 1.0f));

                if (request.IsError)
                {
                    yield break;
                }

                haircuts = ReorderHaircutIds(haircutsRequest.Result);
                cachedHaircuts[avatarCode] = haircuts;
            }
            request.IsDone = true;
            request.Result = haircuts;
        }
        /// <summary>
        /// DownloadAndSaveHaircutMeshAsync implementation
        /// </summary>
        private IEnumerator DownloadAndSaveHaircutMeshFunc(AvatarHaircutData haircutData, AsyncRequest request)
        {
            Debug.LogFormat("Downloading haircut mesh, texture and points simultaneously...");
            var haircutMeshRequest    = connection.DownloadHaircutMeshZipAsync(haircutData);
            var haircutTextureRequest = connection.DownloadHaircutTextureBytesAsync(haircutData);

            yield return(request.AwaitSubrequests(0.8f, haircutMeshRequest, haircutTextureRequest));

            if (request.IsError)
            {
                yield break;
            }

            Debug.LogFormat("Saving haircut mesh and texture to disk...");
            var saveHaircutMeshRequest    = CoreTools.SaveHaircutFileAsync(haircutMeshRequest.Result, haircutData.identity, HaircutFile.HAIRCUT_MESH_ZIP);
            var saveHaircutTextureRequest = CoreTools.SaveHaircutFileAsync(haircutTextureRequest.Result, haircutData.identity, HaircutFile.HAIRCUT_TEXTURE);

            yield return(request.AwaitSubrequests(0.9f, saveHaircutMeshRequest, saveHaircutTextureRequest));

            if (request.IsError)
            {
                yield break;
            }

            Debug.LogFormat("Unzip haircut mesh...");
            var unzipMeshRequest = CoreTools.UnzipFileAsync(saveHaircutMeshRequest.Result);

            yield return(request.AwaitSubrequest(unzipMeshRequest, 1.0f));

            if (request.IsError)
            {
                yield break;
            }

            request.IsDone = true;
        }
Exemple #13
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;
        }
        /// <summary>
        /// GetHeadMeshAsync implementation
        /// </summary>
        private IEnumerator GetHeadMeshFunc(string avatarCode, bool withBlendshapes, int detailsLevel, AsyncRequest <TexturedMesh> request)
        {
            // Need to verify if this avatar supports Level Of Details
            if (detailsLevel > 0)
            {
                var avatarRequest = GetAvatarAsync(avatarCode);
                yield return(avatarRequest);

                if (avatarRequest.IsError)
                {
                    yield break;
                }
                if (string.Compare(avatarRequest.Result.pipeline, PipelineType.FACE.GetPipelineTypeName()) != 0)
                {
                    Debug.LogWarningFormat("Avatar created by {0} doesn't support Level Of Details. Will be used LOD 0.", avatarRequest.Result.pipeline);
                    detailsLevel = 0;
                }
            }

            string meshFilename    = AvatarSdkMgr.Storage().GetAvatarFilename(avatarCode, AvatarFile.MESH_PLY);
            string textureFilename = AvatarSdkMgr.Storage().GetAvatarFilename(avatarCode, AvatarFile.TEXTURE);

            //If there are no required files, will download them.
            if (!File.Exists(meshFilename) || !File.Exists(textureFilename))
            {
                var avatarRequest = connection.GetAvatarAsync(avatarCode);
                yield return(avatarRequest);

                if (avatarRequest.IsError)
                {
                    yield break;
                }

                var downloadRequest = DownloadAndSaveAvatarModelAsync(avatarRequest.Result, false, withBlendshapes);
                yield return(request.AwaitSubrequest(downloadRequest, 0.5f));

                if (request.IsError)
                {
                    yield break;
                }
            }

            //if there are no blendshapes, will download them
            var  blendshapesDir   = AvatarSdkMgr.Storage().GetAvatarSubdirectory(avatarCode, AvatarSubdirectory.BLENDSHAPES);
            bool blendshapesExist = Directory.GetFiles(blendshapesDir).Length > 0;

            if (withBlendshapes && !blendshapesExist)
            {
                var avatarRequest = connection.GetAvatarAsync(avatarCode);
                yield return(avatarRequest);

                if (avatarRequest.IsError)
                {
                    yield break;
                }

                var downloadBlendshapes = connection.DownloadBlendshapesZipAsync(avatarRequest.Result, BlendshapesFormat.BIN, detailsLevel);
                yield return(request.AwaitSubrequest(downloadBlendshapes, 0.8f));

                if (request.IsError)
                {
                    yield break;
                }

                byte[] blendshapesZipBytes = downloadBlendshapes.Result;
                if (blendshapesZipBytes != null && blendshapesZipBytes.Length > 0)
                {
                    var saveBlendshapesZip = CoreTools.SaveAvatarFileAsync(downloadBlendshapes.Result, avatarCode, AvatarFile.BLENDSHAPES_ZIP);
                    yield return(request.AwaitSubrequest(saveBlendshapesZip, 0.9f));

                    if (request.IsError)
                    {
                        yield break;
                    }

                    var unzipBlendshapes = UnzipBlendshapesAsync(saveBlendshapesZip.Result, avatarCode);
                    yield return(request.AwaitSubrequest(unzipBlendshapes, 0.95f));

                    if (request.IsError)
                    {
                        yield break;
                    }

                    CoreTools.DeleteAvatarFile(avatarCode, AvatarFile.BLENDSHAPES_ZIP);
                }
            }

            // At this point all avatar files are already saved to disk. Let's load the files to Unity.
            var loadAvatarHeadRequest = CoreTools.LoadAvatarHeadFromDiskAsync(avatarCode, withBlendshapes, detailsLevel);

            yield return(request.AwaitSubrequest(loadAvatarHeadRequest, 1.0f));

            if (request.IsError)
            {
                yield break;
            }

            request.Result = loadAvatarHeadRequest.Result;
            request.IsDone = true;
        }