Browse Source

Merge pull request #4015 from bghgary/return-animationGroup

Return animation groups when calling SceneLoader.ImportMesh
David Catuhe 7 years ago
parent
commit
9a87ad77a0

+ 6 - 5
Viewer/src/model/modelLoader.ts

@@ -43,7 +43,7 @@ export class ModelLoader {
         let base = modelConfiguration.root || Tools.GetFolderPath(modelConfiguration.url);
         let plugin = modelConfiguration.loader;
 
-        model.loader = SceneLoader.ImportMesh(undefined, base, filename, this._viewer.scene, (meshes, particleSystems, skeletons) => {
+        model.loader = SceneLoader.ImportMesh(undefined, base, filename, this._viewer.scene, (meshes, particleSystems, skeletons, animationGroups) => {
             meshes.forEach(mesh => {
                 Tags.AddTagsTo(mesh, "viewerMesh");
             });
@@ -51,6 +51,10 @@ export class ModelLoader {
             model.particleSystems = particleSystems;
             model.skeletons = skeletons;
 
+            for (const animationGroup of animationGroups) {
+                model.addAnimationGroup(animationGroup);
+            }
+
             model.initAnimations();
             model.onLoadedObservable.notifyObserversWithPromise(model);
         }, (progressEvent) => {
@@ -63,10 +67,7 @@ export class ModelLoader {
 
         if (model.loader.name === "gltf") {
             let gltfLoader = (<GLTFFileLoader>model.loader);
-            gltfLoader.animationStartMode = 0;
-            gltfLoader.onAnimationGroupLoaded = ag => {
-                model.addAnimationGroup(ag);
-            }
+            gltfLoader.animationStartMode = BABYLON.GLTFLoaderAnimationStartMode.NONE;
         }
 
         model.loadId = this._loadId++;

+ 1 - 0
dist/preview release/what's new.md

@@ -106,6 +106,7 @@
 - Cannon and Oimo are optional dependencies ([RaananW](https://github.com/RaananW))
 - Shadows - Introduces [Normal Bias](https://doc.babylonjs.com/babylon101/shadows#normal-bias-since-32) ([sebavan](https://github.com/sebavan)))
 - Earcut is an external, optional dependency. ([RaananW](https://github.com/RaananW))
+- Return animation groups when calling `SceneLoader.ImportMesh`. ([bghgary](https://github.com/bghgary)]
 
 ## Bug fixes
 

+ 8 - 8
loaders/src/glTF/1.0/babylon.glTFLoader.ts

@@ -1573,7 +1573,6 @@ module BABYLON.GLTF1 {
         public onMeshLoadedObservable = new Observable<AbstractMesh>();
         public onTextureLoadedObservable = new Observable<BaseTexture>();
         public onMaterialLoadedObservable = new Observable<Material>();
-        public onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
         public onCompleteObservable = new Observable<IGLTFLoader>();
         public onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
 
@@ -1582,7 +1581,7 @@ module BABYLON.GLTF1 {
         public dispose(): void {}
         // #endregion
 
-        private _importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string) => void): boolean {
+        private _importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string) => void): boolean {
             scene.useRightHandedSystem = true;
 
             GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
@@ -1632,26 +1631,27 @@ module BABYLON.GLTF1 {
                         postLoad(gltfRuntime);
 
                         if (!GLTFFileLoader.IncrementalLoading && onSuccess) {
-                            onSuccess(meshes, [], skeletons);
+                            onSuccess(meshes, skeletons);
                         }
                     });
                 }, onProgress);
 
                 if (GLTFFileLoader.IncrementalLoading && onSuccess) {
-                    onSuccess(meshes, [], skeletons);
+                    onSuccess(meshes, skeletons);
                 }
             }, onError);
 
             return true;
         }
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return new Promise((resolve, reject) => {
-                this._importMeshAsync(meshesNames, scene, data, rootUrl, (meshes, particleSystems, skeletons) => {
+                this._importMeshAsync(meshesNames, scene, data, rootUrl, (meshes, skeletons) => {
                     resolve({
                         meshes: meshes,
-                        particleSystems: particleSystems,
-                        skeletons: skeletons
+                        particleSystems: [],
+                        skeletons: skeletons,
+                        animationGroups: []
                     });
                 }, onProgress, message => {
                     reject(new Error(message));

+ 38 - 14
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -60,7 +60,6 @@ module BABYLON.GLTF2 {
         public readonly onMeshLoadedObservable = new Observable<AbstractMesh>();
         public readonly onTextureLoadedObservable = new Observable<BaseTexture>();
         public readonly onMaterialLoadedObservable = new Observable<Material>();
-        public readonly onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
         public readonly onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
         public readonly onCompleteObservable = new Observable<IGLTFLoader>();
 
@@ -81,7 +80,7 @@ module BABYLON.GLTF2 {
             this._clear();
         }
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
                 let nodes: Nullable<Array<ILoaderNode>> = null;
 
@@ -111,6 +110,7 @@ module BABYLON.GLTF2 {
                         meshes: this._getMeshes(),
                         particleSystems: [],
                         skeletons: this._getSkeletons(),
+                        animationGroups: this._getAnimationGroups()
                     };
                 });
             });
@@ -363,25 +363,38 @@ module BABYLON.GLTF2 {
             return skeletons;
         }
 
-        private _startAnimations(): void {
+        private _getAnimationGroups(): AnimationGroup[] {
+            const animationGroups = new Array<AnimationGroup>();
+
             const animations = this._gltf.animations;
-            if (!animations) {
-                return;
+            if (animations) {
+                for (const animation of animations) {
+                    if (animation._babylonAnimationGroup) {
+                        animationGroups.push(animation._babylonAnimationGroup);
+                    }
+                }
             }
 
+            return animationGroups;
+        }
+
+        private _startAnimations(): void {
             switch (this.animationStartMode) {
                 case GLTFLoaderAnimationStartMode.NONE: {
                     // do nothing
                     break;
                 }
                 case GLTFLoaderAnimationStartMode.FIRST: {
-                    const animation = animations[0];
-                    animation._babylonAnimationGroup!.start(true);
+                    const babylonAnimationGroups = this._getAnimationGroups();
+                    if (babylonAnimationGroups.length !== 0) {
+                        babylonAnimationGroups[0].start(true);
+                    }
                     break;
                 }
                 case GLTFLoaderAnimationStartMode.ALL: {
-                    for (const animation of animations) {
-                        animation._babylonAnimationGroup!.start(true);
+                    const babylonAnimationGroups = this._getAnimationGroups();
+                    for (const babylonAnimationGroup of babylonAnimationGroups) {
+                        babylonAnimationGroup.start(true);
                     }
                     break;
                 }
@@ -722,6 +735,8 @@ module BABYLON.GLTF2 {
                     babylonMesh.skeleton = skin._babylonSkeleton!;
                 });
 
+                // Ignore the TRS of skinned nodes.
+                // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
                 node._babylonMesh!.parent = this._rootBabylonMesh;
                 node._babylonMesh!.position = Vector3.Zero();
                 node._babylonMesh!.rotationQuaternion = Quaternion.Identity();
@@ -836,8 +851,6 @@ module BABYLON.GLTF2 {
                 promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel._index}`, context, animation, channel, babylonAnimationGroup));
             }
 
-            this.onAnimationGroupLoadedObservable.notifyObservers(babylonAnimationGroup);
-
             return Promise.all(promises).then(() => {
                 babylonAnimationGroup.normalize();
             });
@@ -845,7 +858,13 @@ module BABYLON.GLTF2 {
 
         private _loadAnimationChannelAsync(context: string, animationContext: string, animation: ILoaderAnimation, channel: ILoaderAnimationChannel, babylonAnimationGroup: AnimationGroup): Promise<void> {
             const targetNode = GLTFLoader._GetProperty(`${context}/target/node`, this._gltf.nodes, channel.target.node);
-            if (!targetNode._babylonMesh || targetNode.skin != undefined) {
+            if (!targetNode._babylonMesh) {
+                return Promise.resolve();
+            }
+
+            // Ignore animations targeting TRS of skinned nodes.
+            // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
+            if (targetNode.skin != undefined && channel.target.path !== AnimationChannelTargetPath.WEIGHTS) {
                 return Promise.resolve();
             }
 
@@ -962,10 +981,13 @@ module BABYLON.GLTF2 {
                             outTangent: key.outTangent ? key.outTangent[targetIndex] : undefined
                         })));
 
+                        const multiTarget = new AnimationMultiTarget();
                         this._forEachPrimitive(targetNode, babylonMesh => {
                             const morphTarget = babylonMesh.morphTargetManager!.getTarget(targetIndex);
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, morphTarget);
+                            multiTarget.subTargets.push(morphTarget);
                         });
+
+                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, multiTarget);
                     }
                 }
                 else {
@@ -974,9 +996,11 @@ module BABYLON.GLTF2 {
                     babylonAnimation.setKeys(keys);
 
                     if (targetNode._babylonAnimationTargets) {
+                        const multiTarget = new AnimationMultiTarget();
                         for (const target of targetNode._babylonAnimationTargets) {
-                            babylonAnimationGroup.addTargetedAnimation(babylonAnimation, target);
+                            multiTarget.subTargets.push(target);
                         }
+                        babylonAnimationGroup.addTargetedAnimation(babylonAnimation, multiTarget);
                     }
                 }
             });

+ 28 - 0
loaders/src/glTF/2.0/babylon.glTFLoaderUtilities.ts

@@ -18,4 +18,32 @@ module BABYLON.GLTF2 {
             }
         }
     }
+
+    export class AnimationMultiTarget {
+        public subTargets = new Array<any>();
+
+        public set position(value: Vector3) {
+            for (const subTarget of this.subTargets) {
+                subTarget.position = value;
+            }
+        }
+
+        public set rotationQuaternion(value: Quaternion) {
+            for (const subTarget of this.subTargets) {
+                subTarget.rotationQuaternion = value;
+            }
+        }
+
+        public set scaling(value: Vector3) {
+            for (const subTarget of this.subTargets) {
+                subTarget.scaling = value;
+            }
+        }
+
+        public set influence(value: number) {
+            for (const subTarget of this.subTargets) {
+                subTarget.influence = value;
+            }
+        }
+    }
 }

+ 4 - 18
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -63,14 +63,13 @@ module BABYLON {
         onMeshLoadedObservable: Observable<AbstractMesh>;
         onTextureLoadedObservable: Observable<BaseTexture>;
         onMaterialLoadedObservable: Observable<Material>;
-        onAnimationGroupLoadedObservable: Observable<AnimationGroup>;
         onCompleteObservable: Observable<IGLTFLoader>;
         onDisposeObservable: Observable<IGLTFLoader>;
         onExtensionLoadedObservable: Observable<IGLTFLoaderExtension>;
 
         state: Nullable<GLTFLoaderState>;
 
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[] }>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
         loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
     }
 
@@ -172,19 +171,6 @@ module BABYLON {
         }
 
         /**
-         * Raised when the loader creates an animation group after parsing the glTF properties of the animation.
-         */
-        public readonly onAnimationGroupLoadedObservable = new Observable<AnimationGroup>();
-
-        private _onAnimationGroupLoadedObserver: Nullable<Observer<AnimationGroup>>;
-        public set onAnimationGroupLoaded(callback: (animationGroup: AnimationGroup) => void) {
-            if (this._onAnimationGroupLoadedObserver) {
-                this.onAnimationGroupLoadedObservable.remove(this._onAnimationGroupLoadedObserver);
-            }
-            this._onAnimationGroupLoadedObserver = this.onAnimationGroupLoadedObservable.add(callback);
-        }
-
-        /**
          * Raised when the asset is completely loaded, immediately before the loader is disposed.
          * For assets with LODs, raised when all of the LODs are complete.
          * For assets without LODs, raised when the model is complete, immediately after onSuccess.
@@ -273,7 +259,7 @@ module BABYLON {
             this.onDisposeObservable.clear();
         }
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
@@ -294,10 +280,11 @@ module BABYLON {
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 return this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(result => {
-                    var container = new AssetContainer(scene);
+                    const container = new AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                     Array.prototype.push.apply(container.skeletons, result.skeletons);
+                    Array.prototype.push.apply(container.animationGroups, result.animationGroups);
                     container.removeAllFromScene();
                     return container;
                 });
@@ -373,7 +360,6 @@ module BABYLON {
             loader.onTextureLoadedObservable.add(texture => this.onTextureLoadedObservable.notifyObservers(texture));
             loader.onMaterialLoadedObservable.add(material => this.onMaterialLoadedObservable.notifyObservers(material));
             loader.onExtensionLoadedObservable.add(extension => this.onExtensionLoadedObservable.notifyObservers(extension));
-            loader.onAnimationGroupLoadedObservable.add(animationGroup => this.onAnimationGroupLoadedObservable.notifyObservers(animationGroup));
 
             loader.onCompleteObservable.add(() => {
                 this.onMeshLoadedObservable.clear();

File diff suppressed because it is too large
+ 12 - 11
src/Loading/babylon.sceneLoader.ts


+ 71 - 41
src/babylon.assetContainer.ts

@@ -22,12 +22,16 @@ module BABYLON {
         /**
          * ParticleSystems to keep.
          */
-        public particleSystems = new Array<ParticleSystem>();
+        public particleSystems = new Array<IParticleSystem>();
         /**
          * Animations to keep.
          */
         public animations = new Array<Animation>();
         /**
+         * AnimationGroups to keep.
+         */
+        public animationGroups = new Array<AnimationGroup>();
+        /**
          * MultiMaterials to keep.
          */
         public multiMaterials = new Array<MultiMaterial>();
@@ -63,6 +67,10 @@ module BABYLON {
          * Sounds to keep.
          */
         public sounds = new Array<Sound>();
+        /**
+         * Textures to keep.
+         */
+        public textures = new Array<Texture>();
     }
 
     /**
@@ -100,6 +108,10 @@ module BABYLON {
          */
         public animations = new Array<Animation>();
         /**
+         * AnimationGroups populated in the container.
+         */
+        public animationGroups = new Array<AnimationGroup>();
+        /**
          * MultiMaterials populated in the container.
          */
         public multiMaterials = new Array<MultiMaterial>();
@@ -135,115 +147,131 @@ module BABYLON {
          * Sounds populated in the container.
          */
         public sounds = new Array<Sound>();
-        
+        /**
+         * Textures populated in the container.
+         */
+        public textures = new Array<Texture>();
+
         /**
          * Instantiates an AssetContainer.
          * @param scene The scene the AssetContainer belongs to.
          */
-        constructor(scene:Scene){
+        constructor(scene: Scene) {
             this.scene = scene;
         }
-        
+
         /**
          * Adds all the assets from the container to the scene.
          */
-        public addAllToScene(){
-            this.cameras.forEach((o)=>{
+        public addAllToScene() {
+            this.cameras.forEach((o) => {
                 this.scene.addCamera(o);
             });
-            this.lights.forEach((o)=>{
+            this.lights.forEach((o) => {
                 this.scene.addLight(o);
             });
-            this.meshes.forEach((o)=>{
+            this.meshes.forEach((o) => {
                 this.scene.addMesh(o);
             });
-            this.skeletons.forEach((o)=>{
+            this.skeletons.forEach((o) => {
                 this.scene.addSkeleton(o);
             });
-            this.particleSystems.forEach((o)=>{
+            this.particleSystems.forEach((o) => {
                 this.scene.addParticleSystem(o);
             });
-            this.animations.forEach((o)=>{
+            this.animations.forEach((o) => {
                 this.scene.addAnimation(o);
             });
-            this.multiMaterials.forEach((o)=>{
+            this.animationGroups.forEach((o) => {
+                this.scene.addAnimationGroup(o);
+            });
+            this.multiMaterials.forEach((o) => {
                 this.scene.addMultiMaterial(o);
             });
-            this.materials.forEach((o)=>{
+            this.materials.forEach((o) => {
                 this.scene.addMaterial(o);
             });
-            this.morphTargetManagers.forEach((o)=>{
+            this.morphTargetManagers.forEach((o) => {
                 this.scene.addMorphTargetManager(o);
             });
-            this.geometries.forEach((o)=>{
+            this.geometries.forEach((o) => {
                 this.scene.addGeometry(o);
             });
-            this.transformNodes.forEach((o)=>{
+            this.transformNodes.forEach((o) => {
                 this.scene.addTransformNode(o);
             });
-            this.lensFlareSystems.forEach((o)=>{
+            this.lensFlareSystems.forEach((o) => {
                 this.scene.addLensFlareSystem(o);
             });
-            this.actionManagers.forEach((o)=>{
+            this.actionManagers.forEach((o) => {
                 this.scene.addActionManager(o);
             });
-            this.sounds.forEach((o)=>{
+            this.sounds.forEach((o) => {
                 o.play();
-                o.autoplay=true;
+                o.autoplay = true;
                 this.scene.mainSoundTrack.AddSound(o);
-            })
+            });
+            this.textures.forEach((o) => {
+                this.scene.addTexture
+            });
         }
 
         /**
          * Removes all the assets in the container from the scene
          */
-        public removeAllFromScene(){
-            this.cameras.forEach((o)=>{
+        public removeAllFromScene() {
+            this.cameras.forEach((o) => {
                 this.scene.removeCamera(o);
             });
-            this.lights.forEach((o)=>{
+            this.lights.forEach((o) => {
                 this.scene.removeLight(o);
             });
-            this.meshes.forEach((o)=>{
+            this.meshes.forEach((o) => {
                 this.scene.removeMesh(o);
             });
-            this.skeletons.forEach((o)=>{
+            this.skeletons.forEach((o) => {
                 this.scene.removeSkeleton(o);
             });
-            this.particleSystems.forEach((o)=>{
+            this.particleSystems.forEach((o) => {
                 this.scene.removeParticleSystem(o);
             });
-            this.animations.forEach((o)=>{
+            this.animations.forEach((o) => {
                 this.scene.removeAnimation(o);
             });
-            this.multiMaterials.forEach((o)=>{
+            this.animationGroups.forEach((o) => {
+                this.scene.removeAnimationGroup(o);
+            });
+            this.multiMaterials.forEach((o) => {
                 this.scene.removeMultiMaterial(o);
             });
-            this.materials.forEach((o)=>{
+            this.materials.forEach((o) => {
                 this.scene.removeMaterial(o);
             });
-            this.morphTargetManagers.forEach((o)=>{
+            this.morphTargetManagers.forEach((o) => {
                 this.scene.removeMorphTargetManager(o);
             });
-            this.geometries.forEach((o)=>{
+            this.geometries.forEach((o) => {
                 this.scene.removeGeometry(o);
             });
-            this.transformNodes.forEach((o)=>{
+            this.transformNodes.forEach((o) => {
                 this.scene.removeTransformNode(o);
             });
-            this.lensFlareSystems.forEach((o)=>{
+            this.lensFlareSystems.forEach((o) => {
                 this.scene.removeLensFlareSystem(o);
             });
-            this.actionManagers.forEach((o)=>{
+            this.actionManagers.forEach((o) => {
                 this.scene.removeActionManager(o);
             });
-            this.sounds.forEach((o)=>{
+            this.sounds.forEach((o) => {
                 o.stop();
                 o.autoplay = false;
                 this.scene.mainSoundTrack.RemoveSound(o);
-            })
+            });
+            this.textures.forEach((o) => {
+                this.scene.removeTexture(o);
+            });
         }
-        
+
         private _moveAssets<T>(sourceAssets: T[], targetAssets: T[], keepAssets: T[]): void {
             for (let asset of sourceAssets) {
                 let move = true;
@@ -253,7 +281,7 @@ module BABYLON {
                         break;
                     }
                 }
-    
+
                 if (move) {
                     targetAssets.push(asset);
                 }
@@ -269,13 +297,14 @@ module BABYLON {
             if (keepAssets === undefined) {
                 keepAssets = new KeepAssets();
             }
-    
+
             this._moveAssets(this.scene.cameras, this.cameras, keepAssets.cameras);
             this._moveAssets(this.scene.meshes, this.meshes, keepAssets.meshes);
             this._moveAssets(this.scene.getGeometries(), this.geometries, keepAssets.geometries);
             this._moveAssets(this.scene.materials, this.materials, keepAssets.materials);
             this._moveAssets(this.scene._actionManagers, this.actionManagers, keepAssets.actionManagers);
             this._moveAssets(this.scene.animations, this.animations, keepAssets.animations);
+            this._moveAssets(this.scene.animationGroups, this.animationGroups, keepAssets.animationGroups);
             this._moveAssets(this.scene.lensFlareSystems, this.lensFlareSystems, keepAssets.lensFlareSystems);
             this._moveAssets(this.scene.lights, this.lights, keepAssets.lights);
             this._moveAssets(this.scene.morphTargetManagers, this.morphTargetManagers, keepAssets.morphTargetManagers);
@@ -284,7 +313,8 @@ module BABYLON {
             this._moveAssets(this.scene.particleSystems, this.particleSystems, keepAssets.particleSystems);
             this._moveAssets(this.scene.mainSoundTrack.soundCollection, this.sounds, keepAssets.sounds);
             this._moveAssets(this.scene.transformNodes, this.transformNodes, keepAssets.transformNodes);
-    
+            this._moveAssets(this.scene.textures, this.textures, keepAssets.textures);
+
             this.removeAllFromScene();
         }
     }

+ 77 - 30
src/babylon.scene.ts

@@ -2499,44 +2499,75 @@
                 this.particleSystems.splice(index, 1);
             }
             return index;
-        };
+        }
+
         public removeAnimation(toRemove: Animation): number {
             var index = this.animations.indexOf(toRemove);
             if (index !== -1) {
                 this.animations.splice(index, 1);
             }
             return index;
-        };
+        }
+
+        /**
+         * Removes the given animation group from this scene.
+         * @param toRemove The animation group to remove
+         * @returns The index of the removed animation group
+         */
+        public removeAnimationGroup(toRemove: AnimationGroup): number {
+            var index = this.animationGroups.indexOf(toRemove);
+            if (index !== -1) {
+                this.animationGroups.splice(index, 1);
+            }
+            return index;
+        }
+
         public removeMultiMaterial(toRemove: MultiMaterial): number {
             var index = this.multiMaterials.indexOf(toRemove);
             if (index !== -1) {
                 this.multiMaterials.splice(index, 1);
             }
             return index;
-        };
+        }
+
         public removeMaterial(toRemove: Material): number {
             var index = this.materials.indexOf(toRemove);
             if (index !== -1) {
                 this.materials.splice(index, 1);
             }
             return index;
-        };
-        public removeLensFlareSystem(toRemove: LensFlareSystem) {
+        }
+
+        public removeLensFlareSystem(toRemove: LensFlareSystem): number {
             var index = this.lensFlareSystems.indexOf(toRemove);
             if (index !== -1) {
                 this.lensFlareSystems.splice(index, 1);
             }
             return index;
-        };
-        public removeActionManager(toRemove: ActionManager) {
+        }
+
+        public removeActionManager(toRemove: ActionManager): number {
             var index = this._actionManagers.indexOf(toRemove);
             if (index !== -1) {
                 this._actionManagers.splice(index, 1);
             }
             return index;
-        };
+        }
 
-        public addLight(newLight: Light) {
+        /**
+         * Removes the given texture from this scene.
+         * @param toRemove The texture to remove
+         * @returns The index of the removed texture
+         */
+        public removeTexture(toRemove: BaseTexture): number {
+            var index = this.textures.indexOf(toRemove);
+            if (index !== -1) {
+                this.textures.splice(index, 1);
+            }
+            return index;
+        }
+
+        public addLight(newLight: Light): void {
             this.lights.push(newLight);
             this.sortLightsByPriority();
 
@@ -2557,53 +2588,69 @@
             }
         }
 
-        public addCamera(newCamera: Camera) {
+        public addCamera(newCamera: Camera): void {
             this.cameras.push(newCamera);
             this.onNewCameraAddedObservable.notifyObservers(newCamera);
         }
 
-        public addSkeleton(newSkeleton: Skeleton) {
-            this.skeletons.push(newSkeleton)
+        public addSkeleton(newSkeleton: Skeleton): void {
+            this.skeletons.push(newSkeleton);
+        }
+
+        public addParticleSystem(newParticleSystem: IParticleSystem): void {
+            this.particleSystems.push(newParticleSystem);
         }
 
-        public addParticleSystem(newParticleSystem: IParticleSystem) {
-            this.particleSystems.push(newParticleSystem)
+        public addAnimation(newAnimation: Animation): void {
+            this.animations.push(newAnimation);
         }
 
-        public addAnimation(newAnimation: Animation) {
-            this.animations.push(newAnimation)
+        /**
+         * Adds the given animation group to this scene.
+         * @param newAnimationGroup The animation group to add
+         */
+        public addAnimationGroup(newAnimationGroup: AnimationGroup): void {
+            this.animationGroups.push(newAnimationGroup);
         }
 
-        public addMultiMaterial(newMultiMaterial: MultiMaterial) {
-            this.multiMaterials.push(newMultiMaterial)
+        public addMultiMaterial(newMultiMaterial: MultiMaterial): void {
+            this.multiMaterials.push(newMultiMaterial);
         }
 
-        public addMaterial(newMaterial: Material) {
-            this.materials.push(newMaterial)
+        public addMaterial(newMaterial: Material): void {
+            this.materials.push(newMaterial);
         }
 
-        public addMorphTargetManager(newMorphTargetManager: MorphTargetManager) {
-            this.morphTargetManagers.push(newMorphTargetManager)
+        public addMorphTargetManager(newMorphTargetManager: MorphTargetManager): void {
+            this.morphTargetManagers.push(newMorphTargetManager);
         }
 
-        public addGeometry(newGeometrie: Geometry) {
-            this._geometries.push(newGeometrie)
+        public addGeometry(newGeometrie: Geometry): void {
+            this._geometries.push(newGeometrie);
         }
 
-        public addLensFlareSystem(newLensFlareSystem: LensFlareSystem) {
-            this.lensFlareSystems.push(newLensFlareSystem)
+        public addLensFlareSystem(newLensFlareSystem: LensFlareSystem): void {
+            this.lensFlareSystems.push(newLensFlareSystem);
         }
 
-        public addActionManager(newActionManager: ActionManager) {
-            this._actionManagers.push(newActionManager)
+        public addActionManager(newActionManager: ActionManager): void {
+            this._actionManagers.push(newActionManager);
+        }
+
+        /**
+         * Adds the given texture to this scene.
+         * @param newTexture The texture to add
+         */
+        public addTexture(newTexture: BaseTexture): void {
+            this.textures.push(newTexture);
         }
 
         /**
          * Switch active camera
          * @param {Camera} newCamera - new active camera
-		 * @param {boolean} attachControl - call attachControl for the new active camera (default: true)
+         * @param {boolean} attachControl - call attachControl for the new active camera (default: true)
          */
-        public switchActiveCamera(newCamera: Camera, attachControl = true) {
+        public switchActiveCamera(newCamera: Camera, attachControl = true): void {
             var canvas = this._engine.getRenderingCanvas();
 
             if (!canvas) {

+ 23 - 5
tests/unit/babylon/src/Loading/babylon.sceneLoader.tests.ts

@@ -62,6 +62,7 @@ describe('Babylon Scene Loader', function () {
                 expect(result.meshes.length, "meshes.length").to.equal(scene.meshes.length);
                 expect(result.particleSystems.length, "particleSystems.length").to.equal(0);
                 expect(result.skeletons.length, "skeletons.length").to.equal(0);
+                expect(result.animationGroups.length, "animationGroups.length").to.equal(0);
             });
         });
 
@@ -205,9 +206,7 @@ describe('Babylon Scene Loader', function () {
         it('Load Alien', () => {
             const scene = new BABYLON.Scene(subject);
             return BABYLON.SceneLoader.ImportMeshAsync(null, "/Playground/scenes/Alien/", "Alien.gltf", scene).then(result => {
-                expect(result.skeletons.length, "skeletons.length").to.equal(scene.skeletons.length);
-
-                const mapping = {
+                const skeletonsMapping = {
                     "AlienHead": "skeleton0",
                     "Collar": "skeleton1",
                     "LeftEye": "skeleton2",
@@ -218,10 +217,29 @@ describe('Babylon Scene Loader', function () {
                     "Teeth": "skeleton1",
                 };
 
-                for (const meshName in mapping) {
-                    const skeletonName = mapping[meshName];
+                expect(scene.skeletons, "scene.skeletons").to.have.lengthOf(4);
+                expect(result.skeletons, "skeletons").to.have.lengthOf(4);
+
+                for (const meshName in skeletonsMapping) {
+                    const skeletonName = skeletonsMapping[meshName];
                     expect(scene.getMeshByName(meshName).skeleton.name, `skeleton name of mesh '${meshName}'`).to.equal(skeletonName);
                 }
+
+                const alienHeadMesh = scene.getMeshByName("AlienHead") as BABYLON.Mesh;
+                expect(alienHeadMesh.morphTargetManager.numTargets, "alienHeadMesh.morphTargetManager.numTargets").to.equal(2);
+
+                expect(scene.animationGroups, "scene.animationGroups").to.have.lengthOf(1);
+                expect(result.animationGroups, "animationGroups").to.have.lengthOf(1);
+
+                const animationGroup = result.animationGroups[0];
+                expect(animationGroup.name, "animationGroup.name").to.equal("TwoTargetBlend");
+                expect(animationGroup.targetedAnimations, "animationGroup.targetedAnimations").to.have.lengthOf(7);
+                const influenceAnimations = animationGroup.targetedAnimations.filter(_ => _.animation.targetProperty === "influence");
+                expect(influenceAnimations, "influenceAnimations").to.have.lengthOf(2);
+                const rotationAnimations = animationGroup.targetedAnimations.filter(_ => _.animation.targetProperty === "rotationQuaternion");
+                expect(rotationAnimations, "rotationAnimations").to.have.lengthOf(4);
+                const positionAnimations = animationGroup.targetedAnimations.filter(_ => _.animation.targetProperty === "position");
+                expect(positionAnimations, "positionAnimations").to.have.lengthOf(1);
             });
         });