/// <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; }
/// <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; }
/// <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; }