private void ProcessCreatedActors(CreateActor originalMessage, IList <Actor> createdActors, Action onCompleteCallback)
        {
            var guids     = new DeterministicGuids(originalMessage.Actor?.Id);
            var rootActor = createdActors.FirstOrDefault();

            if (rootActor.transform.parent == null)
            {
                // Delete entire hierarchy as we no longer have a valid parent actor for the root of this hierarchy.  It was likely
                // destroyed in the process of the async operation before this callback was called.
                foreach (var actor in createdActors)
                {
                    actor.Destroy();
                }

                createdActors.Clear();

                SendCreateActorResponse(
                    originalMessage,
                    failureMessage: "Parent for the actor being created no longer exists.  Cannot create new actor.");
                return;
            }

            ProcessActors(rootActor.transform, rootActor.transform.parent.GetComponent <Actor>());

            rootActor?.ApplyPatch(originalMessage.Actor);
            Actor.ApplyVisibilityUpdate(rootActor);

            _actorManager.UponStable(
                () => SendCreateActorResponse(originalMessage, actors: createdActors, onCompleteCallback: onCompleteCallback));

            void ProcessActors(Transform xfrm, Actor parent)
            {
                // Generate actors for all GameObjects, even if the loader didn't. Only loader-generated
                // actors are returned to the app though. We do this so library objects get enabled/disabled
                // correctly, even if they're not tracked by the app.
                var actor = xfrm.gameObject.GetComponent <Actor>() ?? xfrm.gameObject.AddComponent <Actor>();

                _actorManager.AddActor(guids.Next(), actor);
                _ownedGameObjects.Add(actor.gameObject);

                actor.ParentId = parent?.Id ?? actor.ParentId;
                if (actor.Renderer != null)
                {
                    actor.MaterialId = MREAPI.AppsAPI.AssetCache.GetId(actor.Renderer.sharedMaterial) ?? Guid.Empty;
                    actor.MeshId     = MREAPI.AppsAPI.AssetCache.GetId(actor.UnityMesh) ?? Guid.Empty;
                }

                foreach (Transform child in xfrm)
                {
                    ProcessActors(child, actor);
                }
            }
        }
        private void ProcessCreatedActors(CreateActor originalMessage, IList <Actor> createdActors, Action onCompleteCallback)
        {
            var guids     = new DeterministicGuids(originalMessage.Actor?.Id);
            var rootActor = createdActors.FirstOrDefault();

            ProcessActors(rootActor.transform, rootActor.transform.parent.GetComponent <Actor>());

            rootActor?.ApplyPatch(originalMessage.Actor);
            Actor.ApplyVisibilityUpdate(rootActor);

            foreach (var actor in createdActors)
            {
                actor.AddSubscriptions(originalMessage.Subscriptions);
            }

            SendCreateActorResponse(originalMessage, actors: createdActors, onCompleteCallback: onCompleteCallback);

            void ProcessActors(Transform xfrm, Actor parent)
            {
                // Generate actors for all GameObjects, even if the loader didn't. Only loader-generated
                // actors are returned to the app though. We do this so library objects get enabled/disabled
                // correctly, even if they're not tracked by the app.
                var actor = xfrm.gameObject.GetComponent <Actor>() ?? xfrm.gameObject.AddComponent <Actor>();

                _actorManager.AddActor(guids.Next(), actor);
                _ownedGameObjects.Add(actor.gameObject);

                actor.ParentId = parent?.Id ?? actor.ParentId;
                if (actor.Renderer != null)
                {
                    actor.MaterialId = MREAPI.AppsAPI.AssetCache.GetId(actor.Renderer.sharedMaterial) ?? Guid.Empty;
                }

                foreach (Transform child in xfrm)
                {
                    ProcessActors(child, actor);
                }
            }
        }
コード例 #3
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);
        }
        private void ProcessCreatedActors(CreateActor originalMessage, IList <Actor> createdActors, Action onCompleteCallback, string guidSeed = null)
        {
            Guid guidGenSeed;

            if (originalMessage != null)
            {
                guidGenSeed = originalMessage.Actor.Id;
            }
            else
            {
                guidGenSeed = UtilMethods.StringToGuid(guidSeed);
            }
            var guids = new DeterministicGuids(guidGenSeed);

            // find the actors with no actor parents
            var rootActors = GetDistinctTreeRoots(
                createdActors.Select(a => a.gameObject).ToArray()
                ).Select(go => go.GetComponent <Actor>()).ToArray();

            var rootActor    = createdActors.FirstOrDefault();
            var createdAnims = new List <Animation.BaseAnimation>(5);

            if (rootActors.Length == 1 && rootActor.transform.parent == null)
            {
                // Delete entire hierarchy as we no longer have a valid parent actor for the root of this hierarchy.  It was likely
                // destroyed in the process of the async operation before this callback was called.
                foreach (var actor in createdActors)
                {
                    actor.Destroy();
                }

                createdActors.Clear();

                SendCreateActorResponse(
                    originalMessage,
                    failureMessage: "Parent for the actor being created no longer exists.  Cannot create new actor.");
                return;
            }

            var secondPassXfrms = new List <Transform>(2);

            foreach (var root in rootActors)
            {
                ProcessActors(root.transform, root.transform.parent != null ? root.transform.parent.GetComponent <Actor>() : null);
            }
            // some things require the whole hierarchy to have actors on it. run those here
            foreach (var pass2 in secondPassXfrms)
            {
                ProcessActors2(pass2);
            }

            if (originalMessage != null && rootActors.Length == 1)
            {
                rootActor?.ApplyPatch(originalMessage.Actor);
            }
            Actor.ApplyVisibilityUpdate(rootActor);

            _actorManager.UponStable(
                () => SendCreateActorResponse(originalMessage, actors: createdActors, anims: createdAnims, onCompleteCallback: onCompleteCallback));

            void ProcessActors(Transform xfrm, Actor parent)
            {
                // Generate actors for all GameObjects, even if the loader didn't. Only loader-generated
                // actors are returned to the app though. We do this so library objects get enabled/disabled
                // correctly, even if they're not tracked by the app.
                var actor = xfrm.gameObject.GetComponent <Actor>() ?? xfrm.gameObject.AddComponent <Actor>();

                _actorManager.AddActor(guids.Next(), actor);
                _ownedGameObjects.Add(actor.gameObject);

                actor.ParentId = parent?.Id ?? actor.ParentId;
                if (actor.Renderer != null)
                {
                    actor.MaterialId = AssetCache.GetId(actor.Renderer.sharedMaterial) ?? Guid.Empty;
                    actor.MeshId     = AssetCache.GetId(actor.UnityMesh) ?? Guid.Empty;
                }

                // native animation construction requires the whole actor hierarchy to already exist. defer to second pass
                var nativeAnim = xfrm.gameObject.GetComponent <UnityEngine.Animation>();

                if (nativeAnim != null && createdActors.Contains(actor))
                {
                    secondPassXfrms.Add(xfrm);
                }

                foreach (Transform child in xfrm)
                {
                    ProcessActors(child, actor);
                }
            }

            void ProcessActors2(Transform xfrm)
            {
                var actor      = xfrm.gameObject.GetComponent <Actor>();
                var nativeAnim = xfrm.gameObject.GetComponent <UnityEngine.Animation>();

                if (nativeAnim != null && createdActors.Contains(actor))
                {
                    var animTargets = xfrm.gameObject.GetComponent <PrefabAnimationTargets>();
                    int stateIndex  = 0;
                    foreach (AnimationState state in nativeAnim)
                    {
                        var anim = new NativeAnimation(AnimationManager, guids.Next(), nativeAnim, state);
                        anim.TargetIds = animTargets != null
                                                        ? animTargets.GetTargets(xfrm, stateIndex ++, addRootToTargets : true).Select(a => a.Id).ToList()
                                                        : new List <Guid>()
                        {
                            actor.Id
                        };

                        AnimationManager.RegisterAnimation(anim);
                        createdAnims.Add(anim);
                    }
                }
            }
        }
コード例 #5
0
        private void ProcessCreatedActors(CreateActor originalMessage, IList <Actor> createdActors, Action onCompleteCallback, string guidSeed = null)
        {
            Guid guidGenSeed;

            if (originalMessage != null)
            {
                guidGenSeed = originalMessage.Actor.Id;
            }
            else
            {
                guidGenSeed = UtilMethods.StringToGuid(guidSeed);
            }
            var guids = new DeterministicGuids(guidGenSeed);

            // find the actors with no actor parents
            var rootActors = GetDistinctTreeRoots(
                createdActors.ToArray()
                ).Select(go => go as Actor).ToArray();
            var rootActor    = createdActors.FirstOrDefault();
            var createdAnims = new List <Animation.BaseAnimation>(5);

            if (rootActors.Length == 1 && rootActor.GetParent() == null)
            {
                // Delete entire hierarchy as we no longer have a valid parent actor for the root of this hierarchy.  It was likely
                // destroyed in the process of the async operation before this callback was called.
                foreach (var actor in createdActors)
                {
                    actor.Destroy();
                }

                createdActors.Clear();

                SendCreateActorResponse(
                    originalMessage,
                    failureMessage: "Parent for the actor being created no longer exists.  Cannot create new actor.");
                return;
            }

            var secondPassXfrms = new List <Spatial>(2);

            foreach (var root in rootActors)
            {
                ProcessActors(root.Node3D, root.GetParent() as Actor);
            }
            // some things require the whole hierarchy to have actors on it. run those here
            foreach (var pass2 in secondPassXfrms)
            {
                ProcessActors2(pass2);
            }

            if (originalMessage != null && rootActors.Length == 1)
            {
                rootActor?.ApplyPatch(originalMessage.Actor);
            }
            Actor.ApplyVisibilityUpdate(rootActor);

            _actorManager.UponStable(
                () => SendCreateActorResponse(originalMessage, actors: createdActors, anims: createdAnims, onCompleteCallback: onCompleteCallback));

            void ProcessActors(Spatial node3D, Actor parent)
            {
                // Generate actors for all node3D, even if the loader didn't. Only loader-generated
                // actors are returned to the app though. We do this so library objects get enabled/disabled
                // correctly, even if they're not tracked by the app.
                Actor actor = (node3D as Actor) ?? Actor.Instantiate(node3D);

                _actorManager.AddActor(guids.Next(), actor);
                _ownedNodes.Add(actor);

                actor.ParentId = parent?.Id ?? actor.ParentId;

                if (actor.MeshInstance != null)
                {
                    // only overwrite material if there's something in the cache, i.e. not a random library material
                    if (actor.MeshInstance.MaterialOverride != null)
                    {
                        var matId = AssetManager.GetByObject(actor.MeshInstance.MaterialOverride)?.Id;
                        if (matId.HasValue)
                        {
                            actor.MaterialId = matId.Value;
                        }
                    }

                    actor.MeshId = AssetManager.GetByObject(actor.GodotMesh)?.Id ?? Guid.Empty;
                }

                // native animation construction requires the whole actor hierarchy to already exist. defer to second pass
                var nativeAnim = node3D.GetChild <Godot.AnimationPlayer>();

                if (nativeAnim != null && createdActors.Contains(actor))
                {
                    secondPassXfrms.Add(node3D);
                }

                foreach (object node in actor.GetChildren())
                {
                    if (node is Spatial)
                    {
                        ProcessActors((Spatial)node, actor);
                    }
                }
            }

            void ProcessActors2(Spatial node3D)
            {
                var actor           = node3D as Actor;
                var animationPlayer = node3D.GetChild <Godot.AnimationPlayer>();

                if (animationPlayer != null && createdActors.Contains(actor))
                {
                    var animTargets = node3D.GetChild <PrefabAnimationTargets>();
                    int animIndex   = 0;
                    foreach (string animationString in animationPlayer.GetAnimationList())
                    {
                        var anim = new NativeAnimation(AnimationManager, guids.Next(), animationPlayer, animationPlayer.GetAnimation(animationString));
                        anim.TargetIds = animTargets != null
                                                        ? animTargets.GetTargets(node3D, animIndex ++, addRootToTargets : true).Select(a => a.Id).ToList()
                                                        : new List <Guid>()
                        {
                            actor.Id
                        };

                        AnimationManager.RegisterAnimation(anim);
                        createdAnims.Add(anim);
                    }
                }
            }
        }
コード例 #6
0
        private async Task <IList <Asset> > LoadAssetsFromGLTF(AssetSource source, Guid containerId, ColliderType colliderType)
        {
            IList <Asset>      assets        = new List <Asset>();
            DeterministicGuids guidGenerator = new DeterministicGuids(UtilMethods.StringToGuid(
                                                                          $"{containerId}:{source.ParsedUri.AbsoluteUri}"));

            // download file
            UtilMethods.GetUrlParts(source.ParsedUri.AbsoluteUri, out string rootUrl, out string filename);
            var loader = new WebRequestLoader(rootUrl);
            await loader.LoadStream(filename);

            // pre-parse glTF document so we can get a scene count
            // TODO: run this in thread
            GLTF.GLTFParser.ParseJson(loader.LoadedStream, out GLTF.Schema.GLTFRoot gltfRoot);

            GLTFSceneImporter importer =
                MREAPI.AppsAPI.GLTFImporterFactory.CreateImporter(gltfRoot, loader, _asyncHelper, loader.LoadedStream);

            importer.SceneParent = MREAPI.AppsAPI.AssetCache.CacheRootGO().transform;
            importer.Collider    = colliderType.ToGLTFColliderType();

            // load prefabs
            if (gltfRoot.Scenes != null)
            {
                for (var i = 0; i < gltfRoot.Scenes.Count; i++)
                {
                    await importer.LoadSceneAsync(i);

                    GameObject rootObject = importer.LastLoadedScene;
                    rootObject.name = gltfRoot.Scenes[i].Name ?? $"scene:{i}";
                    MWGOTreeWalker.VisitTree(rootObject, (go) =>
                    {
                        go.layer = UnityConstants.ActorLayerIndex;
                    });

                    var def = GenerateAssetPatch(rootObject, guidGenerator.Next());
                    def.Name   = rootObject.name;
                    def.Source = new AssetSource(source.ContainerType, source.Uri, $"scene:{i}");
                    MREAPI.AppsAPI.AssetCache.CacheAsset(rootObject, def.Id, containerId, source);
                    assets.Add(def);
                }
            }

            // 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}");
                    MREAPI.AppsAPI.AssetCache.CacheAsset(texture, asset.Id, containerId, source);
                    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}");
                    MREAPI.AppsAPI.AssetCache.CacheAsset(material, asset.Id, containerId, source);
                    assets.Add(asset);
                }
            }

            importer.Dispose();

            return(assets);
        }
コード例 #7
0
        private async Task <IList <Asset> > LoadAssetsFromGLTF(AssetSource source, Guid containerId, ColliderType colliderType)
        {
            IList <Asset>      assets        = new List <Asset>();
            DeterministicGuids guidGenerator = new DeterministicGuids(UtilMethods.StringToGuid(
                                                                          $"{containerId}:{source.ParsedUri.AbsoluteUri}"));

            // download file
            var rootUrl = URIHelper.GetDirectoryName(source.ParsedUri.AbsoluteUri);
            var loader  = new WebRequestLoader(rootUrl);
            var stream  = await loader.LoadStreamAsync(URIHelper.GetFileFromUri(source.ParsedUri));

            // pre-parse glTF document so we can get a scene count
            // TODO: run this in thread
            GLTF.GLTFParser.ParseJson(stream, out GLTF.Schema.GLTFRoot gltfRoot);
            stream.Position = 0;

            GLTFSceneImporter importer =
                MREAPI.AppsAPI.GLTFImporterFactory.CreateImporter(gltfRoot, loader, _asyncHelper, stream);

            importer.SceneParent = MREAPI.AppsAPI.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}");
                    MREAPI.AppsAPI.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()
                    };
                    MREAPI.AppsAPI.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}");
                    MREAPI.AppsAPI.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}");
                    MREAPI.AppsAPI.AssetCache.CacheAsset(rootObject, def.Id, containerId, source);
                    assets.Add(def);
                }
            }

            importer.Dispose();

            return(assets);
        }