Ver código fonte

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

# Conflicts:
#	dist/preview release/babylon.d.ts
#	dist/preview release/babylon.js
#	dist/preview release/babylon.module.d.ts
#	dist/preview release/babylon.worker.js
#	dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
#	dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
David Catuhe 7 anos atrás
pai
commit
45b7001ed1
30 arquivos alterados com 33282 adições e 28632 exclusões
  1. 7654 7646
      dist/preview release/babylon.d.ts
  2. 69 0
      dist/preview release/babylon.js
  3. 1 1
      dist/preview release/babylon.max.js
  4. 7654 7646
      dist/preview release/babylon.module.d.ts
  5. 69 0
      dist/preview release/babylon.worker.js
  6. 8758 6582
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  7. 55 1
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  8. 1 1
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  9. 8758 6582
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  10. 7 1
      dist/preview release/gui/babylon.gui.min.js
  11. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  12. 5 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  13. 4 0
      dist/preview release/loaders/babylonjs.loaders.min.js
  14. 5 0
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  15. 5 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  16. 3 3
      gui/src/advancedDynamicTexture.ts
  17. 8 8
      gui/src/controls/button.ts
  18. 2 2
      gui/src/controls/checkbox.ts
  19. 6 6
      gui/src/controls/colorpicker.ts
  20. 4 0
      gui/src/controls/container.ts
  21. 32 27
      gui/src/controls/control.ts
  22. 4 4
      gui/src/controls/inputText.ts
  23. 2 2
      gui/src/controls/radioButton.ts
  24. 7 5
      gui/src/controls/slider.ts
  25. 143 99
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  26. 4 0
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  27. 0 6
      loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts
  28. 1 1
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  29. 1 1
      src/Mesh/babylon.mesh.vertexData.ts
  30. 18 4
      src/Tools/babylon.observable.ts

Diferenças do arquivo suprimidas por serem muito extensas
+ 7654 - 7646
dist/preview release/babylon.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 69 - 0
dist/preview release/babylon.js


+ 1 - 1
dist/preview release/babylon.max.js

@@ -68783,7 +68783,7 @@ var BABYLON;
             // Are we presenting in the fullscreen fallback?
             this._fullscreenVRpresenting = false;
             this._scene = scene;
-            if (!this._scene.activeCamera) {
+            if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
                 this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", new BABYLON.Vector3(0, 2, 0), scene);
             }
             else {

Diferenças do arquivo suprimidas por serem muito extensas
+ 7654 - 7646
dist/preview release/babylon.module.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 69 - 0
dist/preview release/babylon.worker.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 8758 - 6582
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 55 - 1
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


+ 1 - 1
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -68783,7 +68783,7 @@ var BABYLON;
             // Are we presenting in the fullscreen fallback?
             this._fullscreenVRpresenting = false;
             this._scene = scene;
-            if (!this._scene.activeCamera) {
+            if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
                 this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", new BABYLON.Vector3(0, 2, 0), scene);
             }
             else {

Diferenças do arquivo suprimidas por serem muito extensas
+ 8758 - 6582
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


Diferenças do arquivo suprimidas por serem muito extensas
+ 7 - 1
dist/preview release/gui/babylon.gui.min.js


+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js

@@ -65,8 +65,8 @@ var INSPECTOR =
 	if(false) {
 		// When the styles change, update the <style> tags
 		if(!content.locals) {
-			module.hot.accept("!!../../../Tools/Gulp/node_modules/css-loader/index.js!./babylon.inspector.css", function() {
-				var newContent = require("!!../../../Tools/Gulp/node_modules/css-loader/index.js!./babylon.inspector.css");
+			module.hot.accept("!!../../../tools/gulp/node_modules/css-loader/index.js!./babylon.inspector.css", function() {
+				var newContent = require("!!../../../tools/gulp/node_modules/css-loader/index.js!./babylon.inspector.css");
 				if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
 				update(newContent);
 			});

Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 4 - 0
dist/preview release/loaders/babylonjs.loaders.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 0
dist/preview release/materialsLibrary/babylonjs.materials.min.js


Diferenças do arquivo suprimidas por serem muito extensas
+ 5 - 1
dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js


+ 3 - 3
gui/src/advancedDynamicTexture.ts

@@ -322,7 +322,7 @@ module BABYLON.GUI {
 
                 if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                     if (this._lastControlOver) {
-                        this._lastControlOver._onPointerOut();
+                        this._lastControlOver._onPointerOut(this._lastControlOver);
                     }
                     
                     this._lastControlOver = null;
@@ -378,7 +378,7 @@ module BABYLON.GUI {
                     this.focusedControl = null;
                 } else if (pi.type === BABYLON.PointerEventTypes.POINTERMOVE) {
                     if (this._lastControlOver) {
-                        this._lastControlOver._onPointerOut();
+                        this._lastControlOver._onPointerOut(this._lastControlOver);
                     }              
                     this._lastControlOver = null;
                 }
@@ -416,7 +416,7 @@ module BABYLON.GUI {
         private _attachToOnPointerOut(scene: Scene): void {
             this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add(() => {
                 if (this._lastControlOver) {
-                    this._lastControlOver._onPointerOut();
+                    this._lastControlOver._onPointerOut(this._lastControlOver);
                 }            
                 this._lastControlOver = null;
 

+ 8 - 8
gui/src/controls/button.ts

@@ -51,8 +51,8 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _onPointerEnter(): boolean {
-            if (!super._onPointerEnter()) {
+        public _onPointerEnter(target: Control): boolean {
+            if (!super._onPointerEnter(target)) {
                 return false;
             }
 
@@ -63,16 +63,16 @@ module BABYLON.GUI {
             return true;
         }
 
-        public _onPointerOut(): void {
+        public _onPointerOut(target: Control): void {
             if (this.pointerOutAnimation) {
                 this.pointerOutAnimation();
             }
 
-            super._onPointerOut();
+            super._onPointerOut(target);
         }
 
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
                 return false;
             }
 
@@ -84,12 +84,12 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _onPointerUp(coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
             if (this.pointerUpAnimation) {
                 this.pointerUpAnimation();
             }
 
-            super._onPointerUp(coordinates, buttonIndex);
+            super._onPointerUp(target, coordinates, buttonIndex);
         }        
 
         // Statics

+ 2 - 2
gui/src/controls/checkbox.ts

@@ -102,8 +102,8 @@ module BABYLON.GUI {
         }
 
         // Events
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
                 return false;
             }
 

+ 6 - 6
gui/src/controls/colorpicker.ts

@@ -362,8 +362,8 @@ module BABYLON.GUI {
             return false;
         }
 
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
                 return false;
             }            
 
@@ -384,19 +384,19 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _onPointerMove(coordinates: Vector2): void {
+        public _onPointerMove(target: Control, coordinates: Vector2): void {
             if (this._pointerIsDown) {
                 this._updateValueFromPointer(coordinates.x, coordinates.y);
             }
 
-            super._onPointerMove(coordinates);
+            super._onPointerMove(target, coordinates);
         }
 
-        protected _onPointerUp (coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp (target: Control, coordinates: Vector2, buttonIndex: number): void {
             this._pointerIsDown = false;
             
             this._host._capturingControl = null;
-            super._onPointerUp(coordinates, buttonIndex);
+            super._onPointerUp(target, coordinates, buttonIndex);
         }     
     }    
 }

+ 4 - 0
gui/src/controls/container.ts

@@ -76,6 +76,8 @@ module BABYLON.GUI {
 
             if (index !== -1) {
                 this._children.splice(index, 1);
+
+                control.parent = null;
             }
 
             this._markAsDirty();
@@ -94,6 +96,8 @@ module BABYLON.GUI {
 
             this._children.push(control);
 
+            control.parent = this;
+
             this._markAsDirty();
         }
 

+ 32 - 27
gui/src/controls/control.ts

@@ -7,6 +7,7 @@ module BABYLON.GUI {
         private _zIndex = 0;
         public _root: Container;
         public _host: AdvancedDynamicTexture;
+        public parent: Container;
         public _currentMeasure = Measure.Empty();
         private _fontFamily = "Arial";
         private _fontStyle = "";
@@ -835,68 +836,72 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _onPointerMove(coordinates: Vector2): void {
-            if (this.onPointerMoveObservable.hasObservers()) {
-                this.onPointerMoveObservable.notifyObservers(coordinates);
-            }
+        public _onPointerMove(target: Control, coordinates: Vector2): void {
+            var canNotify: boolean = this.onPointerMoveObservable.notifyObservers(coordinates, -1, target, this);
+
+            if (canNotify && this.parent != null) this.parent._onPointerMove(target, coordinates);
         }
 
-        protected _onPointerEnter(): boolean {
+        public _onPointerEnter(target: Control): boolean {
             if (this._enterCount !== 0) {
                 return false;
             }
 
             this._enterCount++;
-            if (this.onPointerEnterObservable.hasObservers()) {
-                this.onPointerEnterObservable.notifyObservers(this);
-            }
+
+            var canNotify: boolean = this.onPointerEnterObservable.notifyObservers(this, -1, target, this);
+
+            if (canNotify && this.parent != null) this.parent._onPointerEnter(target);
 
             return true;
         }
 
-        public _onPointerOut(): void {
+        public _onPointerOut(target: Control): void {
             this._enterCount = 0;
-            if (this.onPointerOutObservable.hasObservers()) {
-                this.onPointerOutObservable.notifyObservers(this);
-            }
+
+            var canNotify: boolean = this.onPointerOutObservable.notifyObservers(this, -1, target, this);
+
+            if (canNotify && this.parent != null) this.parent._onPointerOut(target);
         }
 
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
             if (this._downCount !== 0) {
                 return false;
             }
 
-            this._downCount++;            
-            if (this.onPointerDownObservable.hasObservers()) {
-                this.onPointerDownObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex));
-            }
+            this._downCount++;
+
+            var canNotify: boolean = this.onPointerDownObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
+
+            if (canNotify && this.parent != null) this.parent._onPointerDown(target, coordinates, buttonIndex);
 
             return true;
         }
 
-        protected _onPointerUp(coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
             this._downCount = 0;
-            if (this.onPointerUpObservable.hasObservers()) {
-                this.onPointerUpObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex));
-            }           
+            
+            var canNotify: boolean = this.onPointerUpObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
+            
+            if (canNotify && this.parent != null) this.parent._onPointerUp(target, coordinates, buttonIndex);
         }
 
         public forcePointerUp() {
-            this._onPointerUp(Vector2.Zero(), 0);
+            this._onPointerUp(this, Vector2.Zero(), 0);
         }
 
         public _processObservables(type: number, x: number, y: number, buttonIndex: number): boolean {
             this._dummyVector2.copyFromFloats(x, y);
             if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
-                this._onPointerMove(this._dummyVector2);
+                this._onPointerMove(this, this._dummyVector2);
 
                 var previousControlOver = this._host._lastControlOver;
                 if (previousControlOver && previousControlOver !== this) {
-                    previousControlOver._onPointerOut();                
+                    previousControlOver._onPointerOut(this);                
                 }
 
                 if (previousControlOver !== this) {
-                    this._onPointerEnter();
+                    this._onPointerEnter(this);
                 }
 
                 this._host._lastControlOver = this;
@@ -904,7 +909,7 @@ module BABYLON.GUI {
             }
 
             if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                this._onPointerDown(this._dummyVector2, buttonIndex);
+                this._onPointerDown(this, this._dummyVector2, buttonIndex);
                 this._host._lastControlDown = this;
                 this._host._lastPickedControl = this;
                 return true;
@@ -912,7 +917,7 @@ module BABYLON.GUI {
 
             if (type === BABYLON.PointerEventTypes.POINTERUP) {
                 if (this._host._lastControlDown) {
-                    this._host._lastControlDown._onPointerUp(this._dummyVector2, buttonIndex);
+                    this._host._lastControlDown._onPointerUp(this, this._dummyVector2, buttonIndex);
                 }
                 this._host._lastControlDown = null;
                 return true;

+ 4 - 4
gui/src/controls/inputText.ts

@@ -400,8 +400,8 @@ module BABYLON.GUI {
             context.restore();
         }
 
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
                 return false;
             }
 
@@ -417,8 +417,8 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _onPointerUp(coordinates: Vector2, buttonIndex: number): void {
-            super._onPointerUp(coordinates, buttonIndex);
+        public _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void {
+            super._onPointerUp(target, coordinates, buttonIndex);
         }  
 
         public dispose() {

+ 2 - 2
gui/src/controls/radioButton.ts

@@ -131,8 +131,8 @@ module BABYLON.GUI {
         }
 
         // Events
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
                 return false;
             }
             this.isChecked = !this.isChecked;

+ 7 - 5
gui/src/controls/slider.ts

@@ -179,8 +179,8 @@ module BABYLON.GUI {
             this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
         }
 
-        protected _onPointerDown(coordinates: Vector2, buttonIndex: number): boolean {
-            if (!super._onPointerDown(coordinates, buttonIndex)) {
+        public _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean {
+            if (!super._onPointerDown(target, coordinates, buttonIndex)) {
                 return false;
             }
 
@@ -192,17 +192,19 @@ module BABYLON.GUI {
             return true;
         }
 
-        protected _onPointerMove(coordinates: Vector2): void {
+        public _onPointerMove(target: Control, coordinates: Vector2): void {
             if (this._pointerIsDown) {
                 this._updateValueFromPointer(coordinates.x);
             }
+
+            super._onPointerMove(target, coordinates);
         }
 
-        protected _onPointerUp (coordinates: Vector2, buttonIndex: number): void {
+        public _onPointerUp (target: Control, coordinates: Vector2, buttonIndex: number): void {
             this._pointerIsDown = false;
             
             this._host._capturingControl = null;
-            super._onPointerUp(coordinates, buttonIndex);
+            super._onPointerUp(target, coordinates, buttonIndex);
         }         
     }    
 }

+ 143 - 99
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -378,63 +378,47 @@ module BABYLON.GLTF2 {
         }
 
         private _loadMesh(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
-            node.babylonMesh.name = mesh.name || node.babylonMesh.name;
+            node.babylonMesh.name = node.babylonMesh.name || mesh.name;
 
-            var babylonMultiMaterial = new MultiMaterial(node.babylonMesh.name, this._babylonScene);
-            node.babylonMesh.material = babylonMultiMaterial;
-
-            var geometry = new Geometry(node.babylonMesh.name, this._babylonScene, null, false, node.babylonMesh);
-            var vertexData = new VertexData();
-            vertexData.positions = [];
-            vertexData.indices = [];
-
-            var subMeshInfos: { verticesStart: number; verticesCount: number; indicesStart: number; indicesCount: number; loadMaterial: (index: number) => void }[] = [];
-            var numRemainingPrimitives = mesh.primitives.length;
-
-            for (var index = 0; index < mesh.primitives.length; index++) {
-                var primitive = mesh.primitives[index];
-                this._loadPrimitive(context + "/primitives/" + index, node, mesh, primitive, (subVertexData, loadMaterial) => {
-                    subMeshInfos.push({
-                        verticesStart: vertexData.positions.length,
-                        verticesCount: subVertexData.positions.length,
-                        indicesStart: vertexData.indices.length,
-                        indicesCount: subVertexData.indices.length,
-                        loadMaterial: loadMaterial
-                    });
-
-                    vertexData.merge(subVertexData);
-
-                    if (--numRemainingPrimitives === 0) {
-                        geometry.setAllVerticesData(vertexData, false);
+            if (!mesh.primitives || mesh.primitives.length === 0) {
+                throw new Error(context + ": Primitives are missing");
+            }
 
-                        // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
-                        // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
-                        node.babylonMesh.subMeshes = [];
+            this._createMorphTargets(context, node, mesh);
 
-                        for (var index = 0; index < subMeshInfos.length; index++) {
-                            var info = subMeshInfos[index];
-                            SubMesh.AddToMesh(index, info.verticesStart, info.verticesCount, info.indicesStart, info.indicesCount, node.babylonMesh);
-                            info.loadMaterial(index);
-                        }
-                    }
-                });
-            }
-        }
+            this._loadAllVertexDataAsync(context, mesh, () => {
+                this._loadMorphTargets(context, node, mesh);
 
-        private _loadPrimitive(context: string, node: IGLTFNode, mesh: IGLTFMesh, primitive: IGLTFMeshPrimitive, onSuccess: (vertexData: VertexData, loadMaterial: (index: number) => void) => void): void {
-            var subMaterials = (node.babylonMesh.material as MultiMaterial).subMaterials;
+                var primitives = mesh.primitives;
 
-            if (primitive.mode && primitive.mode !== EMeshPrimitiveMode.TRIANGLES) {
-                // TODO: handle other primitive modes
-                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
-            }
+                var vertexData = new VertexData();
+                for (var primitive of primitives) {
+                    vertexData.merge(primitive.vertexData);
+                }
 
-            this._createMorphTargets(node, mesh, primitive);
+                new Geometry(node.babylonMesh.name, this._babylonScene, vertexData, false, node.babylonMesh);
+
+                // TODO: optimize this so that sub meshes can be created without being overwritten after setting vertex data.
+                // Sub meshes must be cleared and created after setting vertex data because of mesh._createGlobalSubMesh.
+                node.babylonMesh.subMeshes = [];
+
+                var verticesStart = 0;
+                var indicesStart = 0;
+                for (var index = 0; index < primitives.length; index++) {
+                    var vertexData = primitives[index].vertexData;
+                    var verticesCount = vertexData.positions.length;
+                    var indicesCount = vertexData.indices.length;
+                    SubMesh.AddToMesh(index, verticesStart, verticesCount, indicesStart, indicesCount, node.babylonMesh);
+                    verticesStart += verticesCount;
+                    indicesStart += indicesCount;
+                };
 
-            this._loadVertexDataAsync(context, mesh, primitive, vertexData => {
-                this._loadMorphTargetsData(context, mesh, primitive, vertexData, node.babylonMesh);
+                var multiMaterial = new MultiMaterial(node.babylonMesh.name, this._babylonScene);
+                node.babylonMesh.material = multiMaterial;
+                var subMaterials = multiMaterial.subMaterials;
+                for (var index = 0; index < primitives.length; index++) {
+                    var primitive = primitives[index];
 
-                var loadMaterial = (index: number) => {
                     if (primitive.material == null) {
                         subMaterials[index] = this._getDefaultMaterial();
                     }
@@ -461,17 +445,34 @@ module BABYLON.GLTF2 {
                         });
                     }
                 };
-
-                onSuccess(vertexData, loadMaterial);
             });
         }
 
+        private _loadAllVertexDataAsync(context: string, mesh: IGLTFMesh, onSuccess: () => void): void {
+            var primitives = mesh.primitives;
+            var numRemainingPrimitives = primitives.length;
+            for (var index = 0; index < primitives.length; index++) {
+                let primitive = primitives[index];
+                this._loadVertexDataAsync(context + "/primitive/" + index, mesh, primitive, vertexData => {
+                    primitive.vertexData = vertexData;
+                    if (--numRemainingPrimitives === 0) {
+                        onSuccess();
+                    }
+                });
+            }
+        }
+
         private _loadVertexDataAsync(context: string, mesh: IGLTFMesh, primitive: IGLTFMeshPrimitive, onSuccess: (vertexData: VertexData) => void): void {
             var attributes = primitive.attributes;
             if (!attributes) {
                 throw new Error(context + ": Attributes are missing");
             }
 
+            if (primitive.mode && primitive.mode !== EMeshPrimitiveMode.TRIANGLES) {
+                // TODO: handle other primitive modes
+                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+            }
+
             var vertexData = new VertexData();
 
             var numRemainingAttributes = Object.keys(attributes).length;
@@ -534,75 +535,118 @@ module BABYLON.GLTF2 {
             }
         }
 
-        private _createMorphTargets(node: IGLTFNode, mesh: IGLTFMesh, primitive: IGLTFMeshPrimitive): void {
-            var targets = primitive.targets;
+        private _createMorphTargets(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
+            var primitives = mesh.primitives;
+
+            var targets = primitives[0].targets;
             if (!targets) {
                 return;
             }
 
-            if (!node.babylonMesh.morphTargetManager) {
-                node.babylonMesh.morphTargetManager = new MorphTargetManager();
+            for (var primitive of primitives) {
+                if (!primitive.targets || primitive.targets.length != targets.length) {
+                    throw new Error(context + ": All primitives are required to list the same number of targets");
+                }
             }
 
+            var morphTargetManager = new MorphTargetManager();
+            node.babylonMesh.morphTargetManager = morphTargetManager;
             for (var index = 0; index < targets.length; index++) {
                 var weight = node.weights ? node.weights[index] : mesh.weights ? mesh.weights[index] : 0;
-                node.babylonMesh.morphTargetManager.addTarget(new MorphTarget("morphTarget" + index, weight));
+                morphTargetManager.addTarget(new MorphTarget("morphTarget" + index, weight));
             }
         }
 
-        private _loadMorphTargetsData(context: string, mesh: IGLTFMesh, primitive: IGLTFMeshPrimitive, vertexData: VertexData, babylonMesh: Mesh): void {
-            var targets = primitive.targets;
-            if (!targets) {
+        private _loadMorphTargets(context: string, node: IGLTFNode, mesh: IGLTFMesh): void {
+            var morphTargetManager = node.babylonMesh.morphTargetManager;
+            if (!morphTargetManager) {
                 return;
             }
 
-            for (var index = 0; index < targets.length; index++) {
-                let babylonMorphTarget = babylonMesh.morphTargetManager.getTarget(index);
-                var attributes = targets[index];
-                for (let attribute in attributes) {
-                    var accessor = GLTFUtils.GetArrayItem(this._gltf.accessors, attributes[attribute]);
-                    if (!accessor) {
-                        throw new Error(context + "/targets/" + index + ": Failed to find attribute '" + attribute + "' accessor " + attributes[attribute]);
+            this._loadAllMorphTargetVertexDataAsync(context, node, mesh, () => {
+                var numTargets = morphTargetManager.numTargets;
+                for (var index = 0; index < numTargets; index++) {
+                    var vertexData = new VertexData();
+                    for (var primitive of mesh.primitives) {
+                        vertexData.merge(primitive.targetsVertexData[index]);
                     }
 
-                    this._loadAccessorAsync("#/accessors/" + accessor.index, accessor, data => {
-                        if (accessor.name) {
-                            babylonMorphTarget.name = accessor.name;
-                        }
+                    var target = morphTargetManager.getTarget(index);
+                    target.setNormals(vertexData.normals);
+                    target.setPositions(vertexData.positions);
+                    target.setTangents(vertexData.tangents);
+                }
+            });
+        }
 
-                        // glTF stores morph target information as deltas while babylon.js expects the final data.
-                        // As a result we have to add the original data to the delta to calculate the final data.
-                        var values = <Float32Array>data;
-                        switch (attribute) {
-                            case "NORMAL":
-                                GLTFUtils.ForEach(values, (v, i) => values[i] += vertexData.normals[i]);
-                                babylonMorphTarget.setNormals(values);
-                                break;
-                            case "POSITION":
-                                GLTFUtils.ForEach(values, (v, i) => values[i] += vertexData.positions[i]);
-                                babylonMorphTarget.setPositions(values);
-                                break;
-                            case "TANGENT":
-                                // Tangent data for morph targets is stored as xyz delta.
-                                // The vertexData.tangent is stored as xyzw.
-                                // So we need to skip every fourth vertexData.tangent.
-                                for (var i = 0, j = 0; i < values.length; i++, j++) {
-                                    values[i] += vertexData.tangents[j];
-                                    if ((i + 1) % 3 == 0) {
-                                        j++;
-                                    }
-                                }
-                                babylonMorphTarget.setTangents(values);
-                                break;
-                            default:
-                                Tools.Warn("Ignoring unrecognized attribute '" + attribute + "'");
-                                break;
+        private _loadAllMorphTargetVertexDataAsync(context: string, node: IGLTFNode, mesh: IGLTFMesh, onSuccess: () => void): void {
+            var numRemainingTargets = mesh.primitives.length * node.babylonMesh.morphTargetManager.numTargets;
+
+            for (var primitive of mesh.primitives) {
+                var targets = primitive.targets;
+                primitive.targetsVertexData = new Array<VertexData>(targets.length);
+                for (let index = 0; index < targets.length; index++) {
+                    this._loadMorphTargetVertexDataAsync(context + "/targets/" + index, primitive.vertexData, targets[index], vertexData => {
+                        primitive.targetsVertexData[index] = vertexData;
+                        if (--numRemainingTargets === 0) {
+                            onSuccess();
                         }
                     });
                 }
             }
         }
 
+        private _loadMorphTargetVertexDataAsync(context: string, vertexData: VertexData, attributes: { [name: string]: number }, onSuccess: (vertexData: VertexData) => void): void {
+            var targetVertexData = new VertexData();
+
+            var numRemainingAttributes = Object.keys(attributes).length;
+            for (let attribute in attributes) {
+                var accessor = GLTFUtils.GetArrayItem(this._gltf.accessors, attributes[attribute]);
+                if (!accessor) {
+                    throw new Error(context + ": Failed to find attribute '" + attribute + "' accessor " + attributes[attribute]);
+                }
+
+                this._loadAccessorAsync("#/accessors/" + accessor.index, accessor, data => {
+                    // glTF stores morph target information as deltas while babylon.js expects the final data.
+                    // As a result we have to add the original data to the delta to calculate the final data.
+                    var values = <Float32Array>data;
+                    switch (attribute) {
+                        case "NORMAL":
+                            for (var i = 0; i < values.length; i++) {
+                                values[i] += vertexData.normals[i];
+                            }
+                            targetVertexData.normals = values;
+                            break;
+                        case "POSITION":
+                            for (var i = 0; i < values.length; i++) {
+                                values[i] += vertexData.positions[i];
+                            }
+                            targetVertexData.positions = values;
+                            break;
+                        case "TANGENT":
+                            // Tangent data for morph targets is stored as xyz delta.
+                            // The vertexData.tangent is stored as xyzw.
+                            // So we need to skip every fourth vertexData.tangent.
+                            for (var i = 0, j = 0; i < values.length; i++, j++) {
+                                values[i] += vertexData.tangents[j];
+                                if ((i + 1) % 3 == 0) {
+                                    j++;
+                                }
+                            }
+                            targetVertexData.tangents = values;
+                            break;
+                        default:
+                            Tools.Warn("Ignoring unrecognized attribute '" + attribute + "'");
+                            break;
+                    }
+
+                    if (--numRemainingAttributes === 0) {
+                        onSuccess(targetVertexData);
+                    }
+                });
+            }
+        }
+
         private _loadTransform(node: IGLTFNode): void {
             var position: Vector3 = Vector3.Zero();
             var rotation: Quaternion = Quaternion.Identity();
@@ -819,7 +863,7 @@ module BABYLON.GLTF2 {
                     case "influence":
                         getNextOutputValue = () => {
                             var numTargets = targetNode.babylonMesh.morphTargetManager.numTargets;
-                            var value = new Array(numTargets);
+                            var value = new Array<number>(numTargets);
                             for (var i = 0; i < numTargets; i++) {
                                 value[i] = outputData[outputBufferOffset++];
                             }
@@ -1258,7 +1302,7 @@ module BABYLON.GLTF2 {
                     break;
                 case "MASK":
                     babylonMaterial.alphaCutOff = (material.alphaCutoff == null ? 0.5 : material.alphaCutoff);
-                    
+
                     if (colorFactor) {
                         if (colorFactor[3] == 0) {
                             babylonMaterial.alphaCutOff = 1;

+ 4 - 0
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -205,6 +205,10 @@ module BABYLON.GLTF2 {
         material?: number;
         mode?: EMeshPrimitiveMode;
         targets?: { [name: string]: number }[];
+
+        // Runtime values
+        vertexData: VertexData;
+        targetsVertexData: VertexData[];
     }
 
     export interface IGLTFMesh extends IGLTFChildRootProperty {

+ 0 - 6
loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts

@@ -29,12 +29,6 @@ module BABYLON.GLTF2 {
             return bufferView.buffer;
         }
 
-        public static ForEach(view: Uint16Array | Uint32Array | Float32Array, func: (nvalue: number, index: number) => void): void {
-            for (var index = 0; index < view.length; index++) {
-                func(view[index], index);
-            }
-        }
-
         public static ValidateUri(uri: string): boolean {
             return (uri.indexOf("..") === -1);
         }

+ 1 - 1
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -34,7 +34,7 @@ module BABYLON {
         constructor(scene: Scene, public webVROptions: WebVROptions = {}) {
             this._scene = scene;
 
-            if (!this._scene.activeCamera) {
+            if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
                 this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", new BABYLON.Vector3(0, 2, 0), scene);
             }
             else {

+ 1 - 1
src/Mesh/babylon.mesh.vertexData.ts

@@ -337,7 +337,7 @@
             }
 
             if (!source) {
-                if (length === other.length) {
+                if (length === 0 || length === other.length) {
                     return other;
                 }
 

+ 18 - 4
src/Tools/babylon.observable.ts

@@ -8,13 +8,15 @@
         /**
         * If the callback of a given Observer set skipNextObservers to true the following observers will be ignored
         */
-        constructor(mask: number, skipNextObservers = false) {
-            this.initalize(mask, skipNextObservers);
+        constructor(mask: number, skipNextObservers = false, target?: any, currentTarget?: any) {
+            this.initalize(mask, skipNextObservers, target, currentTarget);
         }
 
-        public initalize(mask: number, skipNextObservers = false): EventState {
+        public initalize(mask: number, skipNextObservers = false, target?: any, currentTarget?: any): EventState {
             this.mask = mask;
             this.skipNextObservers = skipNextObservers;
+            this.target = target;
+            this.currentTarget = currentTarget;
             return this;
         }
 
@@ -27,6 +29,16 @@
          * Get the mask value that were used to trigger the event corresponding to this EventState object
          */
         public mask: number;
+
+        /**
+         * The object that originally notified the event
+         */
+        public target?: any;
+        
+        /**
+         * The current object in the bubbling phase
+         */
+        public currentTarget?: any;
     }
 
     /**
@@ -153,9 +165,11 @@
          * @param eventData
          * @param mask
          */
-        public notifyObservers(eventData: T, mask: number = -1): boolean {
+        public notifyObservers(eventData: T, mask: number = -1, target?: any, currentTarget?: any): boolean {
             let state = this._eventState;
             state.mask = mask;
+            state.target = target;
+            state.currentTarget = currentTarget;
             state.skipNextObservers = false;
 
             for (var obs of this._observers) {