/// <summary> /// LoadAvatarHeadFromDiskAsync implementation. /// </summary> private static IEnumerator LoadAvatarHeadFromDisk( string avatarId, bool withBlendshapes, int detailsLevel, AsyncRequest <TexturedMesh> request ) { // loading two files simultaneously var meshDataRequest = LoadDetailedMeshDataFromDiskAsync(avatarId, detailsLevel); var textureBytesRequest = LoadAvatarFileAsync(avatarId, AvatarFile.TEXTURE); yield return(request.AwaitSubrequests(0.6f, meshDataRequest, textureBytesRequest)); if (request.IsError) { yield break; } MeshData meshData = meshDataRequest.Result; var parseTextureTimer = new MeasureTime("Parse texture data"); // at this point we have all data we need to generate a textured mesh var texturedMesh = new TexturedMesh { mesh = CreateMeshFromMeshData(meshData, "HeadMesh"), texture = new Texture2D(0, 0) }; // This actually blocks the main thread for a few frames, which is bad for VR. // To optimize: load jpg/png texture in C++ code in a separate thread and only SetPixels here in Unity. Should be faster. texturedMesh.texture.LoadImage(textureBytesRequest.Result); parseTextureTimer.Stop(); if (withBlendshapes) { // adding blendshapes... using (new MeasureTime("Add blendshapes")) { var addBlendshapesRequest = AddBlendshapesAsync(avatarId, texturedMesh.mesh, meshData.indexMap); yield return(request.AwaitSubrequest(addBlendshapesRequest, 1.0f)); if (addBlendshapesRequest.IsError) { Debug.LogError("Could not add blendshapes!"); } } } request.Result = texturedMesh; request.IsDone = true; }
/// <summary> /// LoadHaircutFromDiskAsync implementation. /// </summary> private static IEnumerator LoadHaircutFromDiskFunc( string avatarCode, string haircutId, AsyncRequest <TexturedMesh> request ) { var loadingTime = Time.realtimeSinceStartup; // start three async request in parallel var haircutTexture = LoadHaircutFileAsync(haircutId, HaircutFile.HAIRCUT_TEXTURE); var haircutMesh = LoadHaircutFileAsync(haircutId, HaircutFile.HAIRCUT_MESH_PLY); var haircutPoints = LoadAvatarHaircutPointcloudFileAsync(avatarCode, haircutId); // wait until mesh and points load yield return(request.AwaitSubrequests(0.4f, haircutMesh, haircutPoints)); if (request.IsError) { yield break; } // we can start another two subrequests, now parsing the ply files var parseHaircutPly = PlyToMeshDataAsync(haircutMesh.Result); var parseHaircutPoints = PlyToPointsAsync(haircutPoints.Result); // await everything else we need for the haircut yield return(request.AwaitSubrequests(0.95f, parseHaircutPly, parseHaircutPoints, haircutTexture)); if (request.IsError) { yield break; } // now we have all data we need to generate a textured mesh var haircutMeshData = ReplacePointCoords(parseHaircutPly.Result, parseHaircutPoints.Result); var texturedMesh = new TexturedMesh(); texturedMesh.mesh = CreateMeshFromMeshData(haircutMeshData, "HaircutMesh"); texturedMesh.texture = new Texture2D(0, 0); texturedMesh.texture.LoadImage(haircutTexture.Result); request.Result = texturedMesh; request.IsDone = true; Debug.LogFormat("Took {0} seconds to load a haircut", Time.realtimeSinceStartup - loadingTime); }