Example #1
0
        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;
                                }
                            }
                        }
                    }
            }
        }
Example #3
0
        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;
                                }
                            }
                        }
                    }
            }
        }
Example #4
0
        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;
                            }
                        }
                    }
            }
        }
Example #5
0
        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);
        }