private async Task <IList <Asset> > LoadAssetsFromGLTF(AssetSource source, Guid containerId, ColliderType colliderType) { WebRequestLoader loader = null; Stream stream = null; source.ParsedUri = new Uri(_app.ServerAssetUri, source.ParsedUri); var rootUri = URIHelper.GetDirectoryName(source.ParsedUri.AbsoluteUri); var cachedVersion = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.GetVersionSync(source.ParsedUri) : await MREAPI.AppsAPI.AssetCache.GetVersion(source.ParsedUri); // Wait asynchronously until the load throttler lets us through. using (var scope = await AssetLoadThrottling.AcquireLoadScope()) { // set up loader loader = new WebRequestLoader(rootUri); if (!string.IsNullOrEmpty(cachedVersion)) { loader.BeforeRequestCallback += (msg) => { if (msg.RequestUri == source.ParsedUri) { msg.Headers.Add("If-None-Match", cachedVersion); } }; } // download root gltf file, check for cache hit try { stream = await loader.LoadStreamAsync(URIHelper.GetFileFromUri(source.ParsedUri)); source.Version = loader.LastResponse.Headers.ETag?.Tag ?? ""; } catch (HttpRequestException) { if (loader.LastResponse.StatusCode == System.Net.HttpStatusCode.NotModified) { source.Version = cachedVersion; } else { throw; } } } IList <Asset> assetDefs = new List <Asset>(30); DeterministicGuids guidGenerator = new DeterministicGuids(UtilMethods.StringToGuid( $"{containerId}:{source.ParsedUri.AbsoluteUri}")); IList <UnityEngine.Object> assets; // fetch assets from glTF stream or cache if (source.Version != cachedVersion) { assets = await LoadGltfFromStream(loader, stream, colliderType); MREAPI.AppsAPI.AssetCache.StoreAssets(source.ParsedUri, assets, source.Version); } else { var assetsEnum = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.LeaseAssetsSync(source.ParsedUri) : await MREAPI.AppsAPI.AssetCache.LeaseAssets(source.ParsedUri); assets = assetsEnum.ToList(); } // catalog assets int textureIndex = 0, meshIndex = 0, materialIndex = 0, prefabIndex = 0; foreach (var asset in assets) { var assetDef = GenerateAssetPatch(asset, guidGenerator.Next()); assetDef.Name = asset.name; string internalId = null; if (asset is UnityEngine.Texture) { internalId = $"texture:{textureIndex++}"; } else if (asset is UnityEngine.Mesh) { internalId = $"mesh:{meshIndex++}"; } else if (asset is UnityEngine.Material) { internalId = $"material:{materialIndex++}"; } else if (asset is GameObject) { internalId = $"scene:{prefabIndex++}"; } assetDef.Source = new AssetSource(source.ContainerType, source.ParsedUri.AbsoluteUri, internalId, source.Version); ColliderGeometry colliderGeo = null; if (asset is UnityEngine.Mesh mesh) { colliderGeo = colliderType == ColliderType.Mesh ? (ColliderGeometry) new MeshColliderGeometry() { MeshId = assetDef.Id } : (ColliderGeometry) new BoxColliderGeometry() { Size = (mesh.bounds.size * 0.8f).CreateMWVector3(), Center = mesh.bounds.center.CreateMWVector3() }; } _app.AssetManager.Set(assetDef.Id, containerId, asset, colliderGeo, assetDef.Source); assetDefs.Add(assetDef); } return(assetDefs); }
public static async Task <FetchResult> LoadTask(MonoBehaviour runner, Uri uri) { FetchResult result = new FetchResult() { Asset = null, FailureMessage = null }; var ifNoneMatch = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.GetVersionSync(uri) : await MREAPI.AppsAPI.AssetCache.GetVersion(uri); runner.StartCoroutine(LoadCoroutine()); // Spin asynchronously until the request completes. while (!result.IsPopulated) { await Task.Delay(10); } // handle caching if (ifNoneMatch != null && result.ReturnCode == 304) { var assets = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.LeaseAssetsSync(uri) : await MREAPI.AppsAPI.AssetCache.LeaseAssets(uri); result.Asset = assets.FirstOrDefault() as T; } else if (result.Asset != null) { MREAPI.AppsAPI.AssetCache.StoreAssets( uri, new UnityEngine.Object[] { result.Asset as UnityEngine.Object }, result.ETag); } return(result); IEnumerator LoadCoroutine() { DownloadHandler handler; if (typeof(T) == typeof(UnityEngine.AudioClip)) { handler = new DownloadHandlerAudioClip(uri, AudioType.UNKNOWN); } else if (typeof(T) == typeof(UnityEngine.Texture)) { handler = new DownloadHandlerTexture(false); } else { result.FailureMessage = $"Unknown download type: {typeof(T)}"; yield break; } // Spin asynchronously until the load throttler would allow us through. while (AssetLoadThrottling.WouldThrottle()) { yield return(null); } using (var scope = new AssetLoadThrottling.AssetLoadScope()) using (var www = new UnityWebRequest(uri, "GET", handler, null)) { if (ifNoneMatch != null) { www.SetRequestHeader("If-None-Match", ifNoneMatch); } yield return(www.SendWebRequest()); if (www.isNetworkError) { result.ReturnCode = -1; result.FailureMessage = www.error; } else { result.ReturnCode = www.responseCode; result.ETag = www.GetResponseHeader("ETag") ?? "unversioned"; if (www.isHttpError) { result.FailureMessage = $"[{www.responseCode}] {uri}"; } else if (www.responseCode >= 200 && www.responseCode <= 299) { if (typeof(T).IsAssignableFrom(typeof(UnityEngine.AudioClip))) { result.Asset = ((DownloadHandlerAudioClip)handler).audioClip as T; } else if (typeof(T).IsAssignableFrom(typeof(UnityEngine.Texture))) { result.Asset = ((DownloadHandlerTexture)handler).texture as T; } } } } } }
public static async Task <FetchResult> LoadTask(MonoBehaviour runner, Uri uri) { // acquire the exclusive right to load this asset if (!await MREAPI.AppsAPI.AssetCache.AcquireLoadingLock(uri)) { throw new TimeoutException("Failed to acquire exclusive loading rights for " + uri); } FetchResult result = new FetchResult() { Asset = null, FailureMessage = null }; var ifNoneMatch = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.TryGetVersionSync(uri) : await MREAPI.AppsAPI.AssetCache.TryGetVersion(uri); // if the cached version is unversioned, i.e. the server doesn't support ETags, don't bother making request if (ifNoneMatch == Constants.UnversionedAssetVersion) { var assets = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.LeaseAssetsSync(uri) : await MREAPI.AppsAPI.AssetCache.LeaseAssets(uri); result.Asset = assets.FirstOrDefault() as T; MREAPI.AppsAPI.AssetCache.ReleaseLoadingLock(uri); return(result); } runner.StartCoroutine(LoadCoroutine()); // Spin asynchronously until the request completes. while (!result.IsPopulated) { await Task.Delay(10); } // handle caching if (!string.IsNullOrEmpty(ifNoneMatch) && result.ReturnCode == 304) { var assets = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.LeaseAssetsSync(uri) : await MREAPI.AppsAPI.AssetCache.LeaseAssets(uri); result.Asset = assets.FirstOrDefault() as T; } else if (result.Asset != null) { MREAPI.AppsAPI.AssetCache.StoreAssets( uri, new UnityEngine.Object[] { result.Asset }, result.ETag); } MREAPI.AppsAPI.AssetCache.ReleaseLoadingLock(uri); return(result); IEnumerator LoadCoroutine() { DownloadHandler handler; if (typeof(T) == typeof(UnityEngine.AudioClip)) { handler = new DownloadHandlerAudioClip(uri, AudioType.UNKNOWN); } else if (typeof(T) == typeof(UnityEngine.Texture)) { handler = new DownloadHandlerTexture(false); } else { result.FailureMessage = $"Unknown download type: {typeof(T)}"; yield break; } // Spin asynchronously until the load throttler would allow us through. while (AssetLoadThrottling.WouldThrottle()) { yield return(null); } using (var scope = new AssetLoadThrottling.AssetLoadScope()) using (var www = new UnityWebRequest(uri, "GET", handler, null)) { if (!string.IsNullOrEmpty(ifNoneMatch)) { www.SetRequestHeader("If-None-Match", ifNoneMatch); } yield return(www.SendWebRequest()); if (www.isNetworkError) { result.ReturnCode = -1; result.FailureMessage = www.error; } else { result.ReturnCode = www.responseCode; result.ETag = www.GetResponseHeader("ETag") ?? Constants.UnversionedAssetVersion; if (www.isHttpError) { result.FailureMessage = $"[{www.responseCode}] {uri}"; } else if (www.responseCode >= 200 && www.responseCode <= 299) { if (typeof(T).IsAssignableFrom(typeof(UnityEngine.AudioClip))) { result.Asset = ((DownloadHandlerAudioClip)handler).audioClip as T; } else if (typeof(T).IsAssignableFrom(typeof(UnityEngine.Texture))) { result.Asset = ((DownloadHandlerTexture)handler).texture as T; } } } } } }
public static async Task <FetchResult> LoadTask(MonoBehaviour runner, Uri uri) { FetchResult result = new FetchResult() { Asset = null, FailureMessage = null }; runner.StartCoroutine(LoadCoroutine()); // Spin asynchronously until the request completes. while (!result.IsPopulated) { await Task.Delay(10); } return(result); IEnumerator LoadCoroutine() { DownloadHandler handler; if (typeof(T) == typeof(UnityEngine.AudioClip)) { handler = new DownloadHandlerAudioClip(uri, AudioType.UNKNOWN); } else if (typeof(T) == typeof(UnityEngine.Texture)) { handler = new DownloadHandlerTexture(false); } else { result.FailureMessage = $"Unknown download type: {typeof(T)}"; yield break; } // Spin asynchronously until the load throttler would allow us through. while (AssetLoadThrottling.WouldThrottle()) { yield return(null); } using (var scope = new AssetLoadThrottling.AssetLoadScope()) using (var www = new UnityWebRequest(uri, "GET", handler, null)) { yield return(www.SendWebRequest()); if (www.isNetworkError) { result.FailureMessage = www.error; } else if (www.isHttpError) { result.FailureMessage = $"[{www.responseCode}] {uri}"; } else { if (typeof(T) == typeof(UnityEngine.AudioClip)) { result.Asset = ((DownloadHandlerAudioClip)handler).audioClip as T; } else if (typeof(T) == typeof(UnityEngine.Texture)) { result.Asset = ((DownloadHandlerTexture)handler).texture as T; } } } } }
public static async Task <FetchResult> LoadTask(Node runner, Uri uri) { // acquire the exclusive right to load this asset if (!await MREAPI.AppsAPI.AssetCache.AcquireLoadingLock(uri)) { throw new TimeoutException("Failed to acquire exclusive loading rights for " + uri); } FetchResult result = new FetchResult() { Asset = null, FailureMessage = null }; var ifNoneMatch = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.TryGetVersionSync(uri) : await MREAPI.AppsAPI.AssetCache.TryGetVersion(uri); // if the cached version is unversioned, i.e. the server doesn't support ETags, don't bother making request if (ifNoneMatch == Constants.UnversionedAssetVersion) { var assets = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.LeaseAssetsSync(uri) : await MREAPI.AppsAPI.AssetCache.LeaseAssets(uri); result.Asset = assets.FirstOrDefault() as T; MREAPI.AppsAPI.AssetCache.ReleaseLoadingLock(uri); return(result); } await LoadCoroutine(runner); // handle caching if (!string.IsNullOrEmpty(ifNoneMatch) && result.ReturnCode == 304) { var assets = MREAPI.AppsAPI.AssetCache.SupportsSync ? MREAPI.AppsAPI.AssetCache.LeaseAssetsSync(uri) : await MREAPI.AppsAPI.AssetCache.LeaseAssets(uri); result.Asset = assets.FirstOrDefault() as T; } else if (result.Asset != null) { MREAPI.AppsAPI.AssetCache.StoreAssets( uri, new Godot.Object[] { result.Asset }, result.ETag); } MREAPI.AppsAPI.AssetCache.ReleaseLoadingLock(uri); return(result); async Task LoadCoroutine(Node runner) { DownloadHandler handler; if (typeof(T) == typeof(Godot.AudioStream)) { handler = new DownloadHandlerAudioStream(uri, AudioType.Unknown); } else if (typeof(T) == typeof(Godot.Texture)) { handler = new DownloadHandlerTexture(uri); } else { result.FailureMessage = $"Unknown download type: {typeof(T)}"; return; } // Spin asynchronously until the load throttler would allow us through. while (AssetLoadThrottling.WouldThrottle()) { await runner.ToSignal(Engine.GetMainLoop(), "idle_frame"); } using (var scope = new AssetLoadThrottling.AssetLoadScope()) using (var www = new GodotWebRequest(uri, HTTPClient.Method.Get, handler)) { if (!string.IsNullOrEmpty(ifNoneMatch)) { www.SetRequestHeader("If-None-Match", ifNoneMatch); } Error error = www.SendWebRequest(); if (error != 0) { result.ReturnCode = -1; result.FailureMessage = error.ToString(); } else { result.ReturnCode = www.GetResponseCode(); result.ETag = www.GetResponseHeader("ETag") ?? Constants.UnversionedAssetVersion; if (result.ReturnCode >= 200 && result.ReturnCode <= 299) { if (typeof(T).IsAssignableFrom(typeof(AudioStream))) { result.Asset = ((DownloadHandlerAudioStream)handler).AudioStream as T; } else if (typeof(T).IsAssignableFrom(typeof(Godot.Texture))) { result.Asset = ((DownloadHandlerTexture)handler).Texture as T; } } else { result.FailureMessage = $"[{result.ReturnCode}] {uri}"; } } } } }
private async Task <IList <Asset> > LoadAssetsFromGLTF(AssetSource source, Guid containerId, ColliderType colliderType) { WebRequestLoader loader = null; Stream stream = null; IList <Asset> assets = new List <Asset>(); DeterministicGuids guidGenerator = new DeterministicGuids(UtilMethods.StringToGuid( $"{containerId}:{source.ParsedUri.AbsoluteUri}")); // Wait asynchronously until the load throttler lets us through. using (var scope = await AssetLoadThrottling.AcquireLoadScope()) { // download file var rootUrl = URIHelper.GetDirectoryName(source.ParsedUri.AbsoluteUri); loader = new WebRequestLoader(rootUrl); stream = await loader.LoadStreamAsync(URIHelper.GetFileFromUri(source.ParsedUri)); } // pre-parse glTF document so we can get a scene count // run this on a threadpool thread so that the Unity main thread is not blocked GLTF.Schema.GLTFRoot gltfRoot = null; try { await Task.Run(() => { GLTF.GLTFParser.ParseJson(stream, out gltfRoot); }); } catch (Exception e) { Debug.LogError(e); } if (gltfRoot == null) { throw new GLTFLoadException("Failed to parse glTF"); } stream.Position = 0; using (GLTFSceneImporter importer = MREAPI.AppsAPI.GLTFImporterFactory.CreateImporter(gltfRoot, loader, _asyncHelper, stream)) { importer.SceneParent = _app.AssetCache.CacheRootGO().transform; importer.Collider = colliderType.ToGLTFColliderType(); // load textures if (gltfRoot.Textures != null) { for (var i = 0; i < gltfRoot.Textures.Count; i++) { await importer.LoadTextureAsync(gltfRoot.Textures[i], i, true); var texture = importer.GetTexture(i); texture.name = gltfRoot.Textures[i].Name ?? $"texture:{i}"; var asset = GenerateAssetPatch(texture, guidGenerator.Next()); asset.Name = texture.name; asset.Source = new AssetSource(source.ContainerType, source.Uri, $"texture:{i}"); _app.AssetCache.CacheAsset(texture, asset.Id, containerId, source); assets.Add(asset); } } // load meshes if (gltfRoot.Meshes != null) { var cancellationSource = new System.Threading.CancellationTokenSource(); for (var i = 0; i < gltfRoot.Meshes.Count; i++) { var mesh = await importer.LoadMeshAsync(i, cancellationSource.Token); mesh.name = gltfRoot.Meshes[i].Name ?? $"mesh:{i}"; var asset = GenerateAssetPatch(mesh, guidGenerator.Next()); asset.Name = mesh.name; asset.Source = new AssetSource(source.ContainerType, source.Uri, $"mesh:{i}"); var colliderGeo = colliderType == ColliderType.Mesh ? (ColliderGeometry) new MeshColliderGeometry() { MeshId = asset.Id } : (ColliderGeometry) new BoxColliderGeometry() { Size = (mesh.bounds.size * 0.8f).CreateMWVector3() }; _app.AssetCache.CacheAsset(mesh, asset.Id, containerId, source, colliderGeo); assets.Add(asset); } } // load materials if (gltfRoot.Materials != null) { for (var i = 0; i < gltfRoot.Materials.Count; i++) { var matdef = gltfRoot.Materials[i]; var material = await importer.LoadMaterialAsync(i); material.name = matdef.Name ?? $"material:{i}"; var asset = GenerateAssetPatch(material, guidGenerator.Next()); asset.Name = material.name; asset.Source = new AssetSource(source.ContainerType, source.Uri, $"material:{i}"); _app.AssetCache.CacheAsset(material, asset.Id, containerId, source); assets.Add(asset); } } // load prefabs if (gltfRoot.Scenes != null) { for (var i = 0; i < gltfRoot.Scenes.Count; i++) { await importer.LoadSceneAsync(i).ConfigureAwait(true); GameObject rootObject = importer.LastLoadedScene; rootObject.name = gltfRoot.Scenes[i].Name ?? $"scene:{i}"; var animation = rootObject.GetComponent <UnityEngine.Animation>(); if (animation != null) { animation.playAutomatically = false; // initialize mapping so we know which gameobjects are targeted by which animation clips var mapping = rootObject.AddComponent <PrefabAnimationTargets>(); mapping.Initialize(gltfRoot, i); } MWGOTreeWalker.VisitTree(rootObject, (go) => { go.layer = MREAPI.AppsAPI.LayerApplicator.DefaultLayer; }); var def = GenerateAssetPatch(rootObject, guidGenerator.Next()); def.Name = rootObject.name; def.Source = new AssetSource(source.ContainerType, source.Uri, $"scene:{i}"); _app.AssetCache.CacheAsset(rootObject, def.Id, containerId, source); assets.Add(def); } } } return(assets); }