瀏覽代碼

Merge https://github.com/BabylonJS/Babylon.js into dualVRControllers

Trevor Baron 7 年之前
父節點
當前提交
1e0ff12e5f
共有 39 個文件被更改,包括 156645 次插入42865 次删除
  1. 11712 11596
      Playground/babylon.d.txt
  2. 12 7
      Tools/Gulp/config.json
  3. 9 4
      Viewer/src/viewer/viewer.ts
  4. 12508 12365
      dist/preview release/babylon.d.ts
  5. 46 45
      dist/preview release/babylon.js
  6. 446 137
      dist/preview release/babylon.max.js
  7. 46 45
      dist/preview release/babylon.worker.js
  8. 12809 12701
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  9. 46 46
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  10. 3001 2743
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  11. 3003 2745
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  12. 448 139
      dist/preview release/es6.js
  13. 22 19
      dist/preview release/gui/babylon.gui.d.ts
  14. 60 47
      dist/preview release/gui/babylon.gui.js
  15. 3 3
      dist/preview release/gui/babylon.gui.min.js
  16. 22 19
      dist/preview release/gui/babylon.gui.module.d.ts
  17. 14 2
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  18. 100 6
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  19. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  20. 14 2
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  21. 100 6
      dist/preview release/loaders/babylon.glTFFileLoader.js
  22. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  23. 91 6
      dist/preview release/loaders/babylonjs.loaders.js
  24. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  25. 14 2
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  26. 486 0
      dist/preview release/viewer/babylon.viewer.d.ts
  27. 58 58
      dist/preview release/viewer/babylon.viewer.js
  28. 110499 0
      dist/preview release/viewer/babylon.viewer.max.js
  29. 486 0
      dist/preview release/viewer/babylon.viewer.module.d.ts
  30. 2 0
      dist/preview release/viewer/package.json
  31. 3 1
      dist/preview release/what's new.md
  32. 11 2
      src/Engine/babylon.engine.ts
  33. 24 0
      src/Particles/babylon.iParticleEmitterType.ts
  34. 44 44
      src/Particles/babylon.boxParticleEmitter.ts
  35. 39 2
      src/Particles/babylon.coneParticleEmitter.ts
  36. 72 1
      src/Particles/babylon.sphereParticleEmitter.ts
  37. 213 47
      src/Particles/babylon.gpuParticleSystem.ts
  38. 75 12
      src/Particles/babylon.particleSystem.ts
  39. 99 5
      src/Shaders/gpuUpdateParticles.vertex.fx

文件差異過大導致無法顯示
+ 11712 - 11596
Playground/babylon.d.txt


+ 12 - 7
Tools/Gulp/config.json

@@ -274,10 +274,10 @@
             "files": [
                 "../../src/Particles/babylon.particle.js",
                 "../../src/Particles/babylon.particleSystem.js",
-                "../../src/Particles/babylon.boxParticleEmitter.js",
-                "../../src/Particles/babylon.coneParticleEmitter.js",
-                "../../src/Particles/babylon.sphereParticleEmitter.js",
-                "../../src/Particles/babylon.iParticleEmitterType.js"
+                "../../src/Particles/EmitterTypes/babylon.boxParticleEmitter.js",
+                "../../src/Particles/EmitterTypes/babylon.coneParticleEmitter.js",
+                "../../src/Particles/EmitterTypes/babylon.sphereParticleEmitter.js",
+                "../../src/Particles/EmitterTypes/babylon.IParticleEmitterType.js"
             ],
             "dependUpon": [
                 "core"
@@ -289,10 +289,11 @@
         },
         "gpuParticles": {
             "files": [
-                "../../src/Particles/babylon.gpuParticleSystem.js"
+                "../../src/Particles/babylon.gpuParticleSystem.js"          
             ],
             "dependUpon": [
-                "core"
+                "core",
+                "particles"
             ],
             "shaders": [
                 "gpuRenderParticles.vertex",
@@ -1699,10 +1700,14 @@
                         {
                             "filename": "viewer.js",
                             "outputDirectory": "/../../Viewer/dist/"
+                        },
+                        {
+                            "filename": "babylon.viewer.max.js",
+                            "outputDirectory": "/viewer/"
                         }
                     ]
                 }
             ]
         }
     }
-}
+}

+ 9 - 4
Viewer/src/viewer/viewer.ts

@@ -32,6 +32,11 @@ export abstract class AbstractViewer {
     protected shadowGeneratorBias: number;
     protected defaultPipelineTextureType: number;
     protected maxShadows: number;
+    private _hdrSupport: boolean;
+
+    public get isHdrSupported() {
+        return this._hdrSupport;
+    }
 
 
     // observables
@@ -222,8 +227,8 @@ export abstract class AbstractViewer {
                         options.groundMirrorFresnelWeight = groundConfig.mirror.fresnelWeight;
                     if (groundConfig.mirror.fallOffDistance !== undefined)
                         options.groundMirrorFallOffDistance = groundConfig.mirror.fallOffDistance;
-                    if (this.defaultHighpTextureType !== undefined)
-                        options.groundMirrorTextureType = this.defaultHighpTextureType;
+                    if (this.defaultPipelineTextureType !== undefined)
+                        options.groundMirrorTextureType = this.defaultPipelineTextureType;
                 }
             }
 
@@ -818,7 +823,7 @@ export abstract class AbstractViewer {
         let linearHalfFloatTargets = caps.textureHalfFloatRender && caps.textureHalfFloatLinearFiltering;
         let linearFloatTargets = caps.textureFloatRender && caps.textureFloatLinearFiltering;
 
-        let supportsHDR: boolean = !!(linearFloatTargets || linearHalfFloatTargets);
+        this._hdrSupport = !!(linearFloatTargets || linearHalfFloatTargets);
 
         if (linearHalfFloatTargets) {
             this.defaultHighpTextureType = Engine.TEXTURETYPE_HALF_FLOAT;
@@ -831,7 +836,7 @@ export abstract class AbstractViewer {
             this.shadowGeneratorBias = 0.001;
         }
 
-        this.defaultPipelineTextureType = supportsHDR ? this.defaultHighpTextureType : Engine.TEXTURETYPE_UNSIGNED_INT;
+        this.defaultPipelineTextureType = this._hdrSupport ? this.defaultHighpTextureType : Engine.TEXTURETYPE_UNSIGNED_INT;
     }
 
     /**

文件差異過大導致無法顯示
+ 12508 - 12365
dist/preview release/babylon.d.ts


文件差異過大導致無法顯示
+ 46 - 45
dist/preview release/babylon.js


文件差異過大導致無法顯示
+ 446 - 137
dist/preview release/babylon.max.js


文件差異過大導致無法顯示
+ 46 - 45
dist/preview release/babylon.worker.js


文件差異過大導致無法顯示
+ 12809 - 12701
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


文件差異過大導致無法顯示
+ 46 - 46
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


文件差異過大導致無法顯示
+ 3001 - 2743
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


文件差異過大導致無法顯示
+ 3003 - 2745
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js


文件差異過大導致無法顯示
+ 448 - 139
dist/preview release/es6.js


+ 22 - 19
dist/preview release/gui/babylon.gui.d.ts

@@ -18,7 +18,9 @@ declare module BABYLON.GUI {
         _lastPickedControl: Control;
         _lastControlOver: Nullable<Control>;
         _lastControlDown: Nullable<Control>;
-        _capturingControl: Nullable<Control>;
+        _capturingControl: {
+            [pointerId: number]: Control;
+        };
         _shouldBlockPointer: boolean;
         _layerToDispose: Nullable<Layer>;
         _linkedControls: Control[];
@@ -49,7 +51,7 @@ declare module BABYLON.GUI {
         _getGlobalViewport(scene: Scene): Viewport;
         private _checkUpdate(camera);
         private _render();
-        private _doPicking(x, y, type, buttonIndex);
+        private _doPicking(x, y, type, pointerId, buttonIndex);
         attach(): void;
         attachToMesh(mesh: AbstractMesh, supportPointerMove?: boolean): void;
         moveFocusToControl(control: IFocusableControl): void;
@@ -189,6 +191,7 @@ declare module BABYLON.GUI {
         private _downCount;
         private _enterCount;
         private _doNotRender;
+        private _downPointerIds;
         isHitTestVisible: boolean;
         isPointerBlocker: boolean;
         isFocusInvisible: boolean;
@@ -299,14 +302,14 @@ declare module BABYLON.GUI {
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         contains(x: number, y: number): boolean;
-        _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean;
+        _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         _onPointerMove(target: Control, coordinates: Vector2): void;
         _onPointerEnter(target: Control): boolean;
         _onPointerOut(target: Control): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
-        forcePointerUp(): void;
-        _processObservables(type: number, x: number, y: number, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
+        forcePointerUp(pointerId?: Nullable<number>): void;
+        _processObservables(type: number, x: number, y: number, pointerId: number, buttonIndex: number): boolean;
         private _prepareFont();
         dispose(): void;
         private static _HORIZONTAL_ALIGNMENT_LEFT;
@@ -357,7 +360,7 @@ declare module BABYLON.GUI {
         protected _localDraw(context: CanvasRenderingContext2D): void;
         _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean;
+        _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         protected _clipForChildren(context: CanvasRenderingContext2D): void;
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         dispose(): void;
@@ -487,9 +490,9 @@ declare module BABYLON.GUI {
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         private _pointerIsDown;
         private _updateValueFromPointer(x, y);
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
         _onPointerMove(target: Control, coordinates: Vector2): void;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
     }
 }
 
@@ -509,7 +512,7 @@ declare module BABYLON.GUI {
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
     }
 }
 
@@ -530,7 +533,7 @@ declare module BABYLON.GUI {
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
     }
 }
 
@@ -699,11 +702,11 @@ declare module BABYLON.GUI {
         pointerUpAnimation: () => void;
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
-        _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean;
+        _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         _onPointerEnter(target: Control): boolean;
         _onPointerOut(target: Control): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
         static CreateImageButton(name: string, text: string, imageUrl: string): Button;
         static CreateImageOnlyButton(name: string, imageUrl: string): Button;
         static CreateSimpleButton(name: string, text: string): Button;
@@ -744,9 +747,9 @@ declare module BABYLON.GUI {
         private _updateValueFromPointer(x, y);
         private _isPointOnSquare(coordinates);
         private _isPointOnWheel(coordinates);
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
         _onPointerMove(target: Control, coordinates: Vector2): void;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
     }
 }
 
@@ -793,8 +796,8 @@ declare module BABYLON.GUI {
         processKey(keyCode: number, key?: string): void;
         processKeyboard(evt: KeyboardEvent): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
         dispose(): void;
     }
 }

+ 60 - 47
dist/preview release/gui/babylon.gui.js

@@ -33,6 +33,7 @@ var BABYLON;
                 var _this = _super.call(this, name, { width: width, height: height }, scene, generateMipMaps, samplingMode, BABYLON.Engine.TEXTUREFORMAT_RGBA) || this;
                 _this._isDirty = false;
                 _this._rootContainer = new GUI.Container("root");
+                _this._capturingControl = {};
                 _this._linkedControls = new Array();
                 _this._isFullscreen = false;
                 _this._fullscreenViewport = new BABYLON.Viewport(0, 0, 1, 1);
@@ -340,7 +341,7 @@ var BABYLON;
                 var measure = new GUI.Measure(0, 0, renderWidth, renderHeight);
                 this._rootContainer._draw(measure, context);
             };
-            AdvancedDynamicTexture.prototype._doPicking = function (x, y, type, buttonIndex) {
+            AdvancedDynamicTexture.prototype._doPicking = function (x, y, type, pointerId, buttonIndex) {
                 var scene = this.getScene();
                 if (!scene) {
                     return;
@@ -351,11 +352,11 @@ var BABYLON;
                     x = x * ((textureSize.width / this._renderScale) / engine.getRenderWidth());
                     y = y * ((textureSize.height / this._renderScale) / engine.getRenderHeight());
                 }
-                if (this._capturingControl) {
-                    this._capturingControl._processObservables(type, x, y, buttonIndex);
+                if (this._capturingControl[pointerId]) {
+                    this._capturingControl[pointerId]._processObservables(type, x, y, pointerId, buttonIndex);
                     return;
                 }
-                if (!this._rootContainer._processPicking(x, y, type, buttonIndex)) {
+                if (!this._rootContainer._processPicking(x, y, type, pointerId, buttonIndex)) {
                     if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                         if (this._lastControlOver) {
                             this._lastControlOver._onPointerOut(this._lastControlOver);
@@ -389,7 +390,7 @@ var BABYLON;
                     var x = (scene.pointerX / engine.getHardwareScalingLevel() - viewport.x * engine.getRenderWidth()) / viewport.width;
                     var y = (scene.pointerY / engine.getHardwareScalingLevel() - viewport.y * engine.getRenderHeight()) / viewport.height;
                     _this._shouldBlockPointer = false;
-                    _this._doPicking(x, y, pi.type, pi.event.button);
+                    _this._doPicking(x, y, pi.type, pi.event.pointerId || 0, pi.event.button);
                     pi.skipOnPointerObservable = _this._shouldBlockPointer;
                 });
                 this._attachToOnPointerOut(scene);
@@ -407,16 +408,17 @@ var BABYLON;
                         && pi.type !== BABYLON.PointerEventTypes.POINTERDOWN) {
                         return;
                     }
+                    var pointerId = pi.event.pointerId || 0;
                     if (pi.pickInfo && pi.pickInfo.hit && pi.pickInfo.pickedMesh === mesh) {
                         var uv = pi.pickInfo.getTextureCoordinates();
                         if (uv) {
                             var size = _this.getSize();
-                            _this._doPicking(uv.x * size.width, (1.0 - uv.y) * size.height, pi.type, pi.event.button);
+                            _this._doPicking(uv.x * size.width, (1.0 - uv.y) * size.height, pi.type, pointerId, pi.event.button);
                         }
                     }
                     else if (pi.type === BABYLON.PointerEventTypes.POINTERUP) {
                         if (_this._lastControlDown) {
-                            _this._lastControlDown.forcePointerUp();
+                            _this._lastControlDown.forcePointerUp(pointerId);
                         }
                         _this._lastControlDown = null;
                         _this.focusedControl = null;
@@ -854,6 +856,7 @@ var BABYLON;
                 this._downCount = 0;
                 this._enterCount = 0;
                 this._doNotRender = false;
+                this._downPointerIds = {};
                 this.isHitTestVisible = true;
                 this.isPointerBlocker = false;
                 this.isFocusInvisible = false;
@@ -1659,14 +1662,14 @@ var BABYLON;
                 }
                 return true;
             };
-            Control.prototype._processPicking = function (x, y, type, buttonIndex) {
+            Control.prototype._processPicking = function (x, y, type, pointerId, buttonIndex) {
                 if (!this.isHitTestVisible || !this.isVisible || this._doNotRender) {
                     return false;
                 }
                 if (!this.contains(x, y)) {
                     return false;
                 }
-                this._processObservables(type, x, y, buttonIndex);
+                this._processObservables(type, x, y, pointerId, buttonIndex);
                 return true;
             };
             Control.prototype._onPointerMove = function (target, coordinates) {
@@ -1690,26 +1693,36 @@ var BABYLON;
                 if (canNotify && this.parent != null)
                     this.parent._onPointerOut(target);
             };
-            Control.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
+            Control.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
                 if (this._downCount !== 0) {
                     return false;
                 }
                 this._downCount++;
+                this._downPointerIds[pointerId] = true;
                 var canNotify = this.onPointerDownObservable.notifyObservers(new GUI.Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
                 if (canNotify && this.parent != null)
-                    this.parent._onPointerDown(target, coordinates, buttonIndex);
+                    this.parent._onPointerDown(target, coordinates, pointerId, buttonIndex);
                 return true;
             };
-            Control.prototype._onPointerUp = function (target, coordinates, buttonIndex) {
+            Control.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex) {
                 this._downCount = 0;
+                delete this._downPointerIds[pointerId];
                 var canNotify = this.onPointerUpObservable.notifyObservers(new GUI.Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
                 if (canNotify && this.parent != null)
-                    this.parent._onPointerUp(target, coordinates, buttonIndex);
+                    this.parent._onPointerUp(target, coordinates, pointerId, buttonIndex);
             };
-            Control.prototype.forcePointerUp = function () {
-                this._onPointerUp(this, BABYLON.Vector2.Zero(), 0);
+            Control.prototype.forcePointerUp = function (pointerId) {
+                if (pointerId === void 0) { pointerId = null; }
+                if (pointerId !== null) {
+                    this._onPointerUp(this, BABYLON.Vector2.Zero(), pointerId, 0);
+                }
+                else {
+                    for (var key in this._downPointerIds) {
+                        this._onPointerUp(this, BABYLON.Vector2.Zero(), +key, 0);
+                    }
+                }
             };
-            Control.prototype._processObservables = function (type, x, y, buttonIndex) {
+            Control.prototype._processObservables = function (type, x, y, pointerId, buttonIndex) {
                 this._dummyVector2.copyFromFloats(x, y);
                 if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                     this._onPointerMove(this, this._dummyVector2);
@@ -1724,14 +1737,14 @@ var BABYLON;
                     return true;
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                    this._onPointerDown(this, this._dummyVector2, buttonIndex);
+                    this._onPointerDown(this, this._dummyVector2, pointerId, buttonIndex);
                     this._host._lastControlDown = this;
                     this._host._lastPickedControl = this;
                     return true;
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERUP) {
                     if (this._host._lastControlDown) {
-                        this._host._lastControlDown._onPointerUp(this, this._dummyVector2, buttonIndex);
+                        this._host._lastControlDown._onPointerUp(this, this._dummyVector2, pointerId, buttonIndex);
                     }
                     this._host._lastControlDown = null;
                     return true;
@@ -2043,7 +2056,7 @@ var BABYLON;
                     this.onAfterDrawObservable.notifyObservers(this);
                 }
             };
-            Container.prototype._processPicking = function (x, y, type, buttonIndex) {
+            Container.prototype._processPicking = function (x, y, type, pointerId, buttonIndex) {
                 if (!this.isVisible || this.notRenderable) {
                     return false;
                 }
@@ -2053,14 +2066,14 @@ var BABYLON;
                 // Checking backwards to pick closest first
                 for (var index = this._children.length - 1; index >= 0; index--) {
                     var child = this._children[index];
-                    if (child._processPicking(x, y, type, buttonIndex)) {
+                    if (child._processPicking(x, y, type, pointerId, buttonIndex)) {
                         return true;
                     }
                 }
                 if (!this.isHitTestVisible) {
                     return false;
                 }
-                return this._processObservables(type, x, y, buttonIndex);
+                return this._processObservables(type, x, y, pointerId, buttonIndex);
             };
             Container.prototype._clipForChildren = function (context) {
                 // DO nothing
@@ -2933,13 +2946,13 @@ var BABYLON;
                 }
                 this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
             };
-            Slider.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
-                if (!_super.prototype._onPointerDown.call(this, target, coordinates, buttonIndex)) {
+            Slider.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
+                if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                     return false;
                 }
                 this._pointerIsDown = true;
                 this._updateValueFromPointer(coordinates.x, coordinates.y);
-                this._host._capturingControl = this;
+                this._host._capturingControl[pointerId] = this;
                 return true;
             };
             Slider.prototype._onPointerMove = function (target, coordinates) {
@@ -2948,10 +2961,10 @@ var BABYLON;
                 }
                 _super.prototype._onPointerMove.call(this, target, coordinates);
             };
-            Slider.prototype._onPointerUp = function (target, coordinates, buttonIndex) {
+            Slider.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex) {
                 this._pointerIsDown = false;
-                this._host._capturingControl = null;
-                _super.prototype._onPointerUp.call(this, target, coordinates, buttonIndex);
+                delete this._host._capturingControl[pointerId];
+                _super.prototype._onPointerUp.call(this, target, coordinates, pointerId, buttonIndex);
             };
             return Slider;
         }(GUI.Control));
@@ -3073,8 +3086,8 @@ var BABYLON;
                 context.restore();
             };
             // Events
-            Checkbox.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
-                if (!_super.prototype._onPointerDown.call(this, target, coordinates, buttonIndex)) {
+            Checkbox.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
+                if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                     return false;
                 }
                 this.isChecked = !this.isChecked;
@@ -3221,8 +3234,8 @@ var BABYLON;
                 context.restore();
             };
             // Events
-            RadioButton.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
-                if (!_super.prototype._onPointerDown.call(this, target, coordinates, buttonIndex)) {
+            RadioButton.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
+                if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                     return false;
                 }
                 this.isChecked = !this.isChecked;
@@ -3929,14 +3942,14 @@ var BABYLON;
                 return "Button";
             };
             // While being a container, the button behaves like a control.
-            Button.prototype._processPicking = function (x, y, type, buttonIndex) {
+            Button.prototype._processPicking = function (x, y, type, pointerId, buttonIndex) {
                 if (!this.isHitTestVisible || !this.isVisible || this.notRenderable) {
                     return false;
                 }
                 if (!_super.prototype.contains.call(this, x, y)) {
                     return false;
                 }
-                this._processObservables(type, x, y, buttonIndex);
+                this._processObservables(type, x, y, pointerId, buttonIndex);
                 return true;
             };
             Button.prototype._onPointerEnter = function (target) {
@@ -3954,8 +3967,8 @@ var BABYLON;
                 }
                 _super.prototype._onPointerOut.call(this, target);
             };
-            Button.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
-                if (!_super.prototype._onPointerDown.call(this, target, coordinates, buttonIndex)) {
+            Button.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
+                if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                     return false;
                 }
                 if (this.pointerDownAnimation) {
@@ -3963,11 +3976,11 @@ var BABYLON;
                 }
                 return true;
             };
-            Button.prototype._onPointerUp = function (target, coordinates, buttonIndex) {
+            Button.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex) {
                 if (this.pointerUpAnimation) {
                     this.pointerUpAnimation();
                 }
-                _super.prototype._onPointerUp.call(this, target, coordinates, buttonIndex);
+                _super.prototype._onPointerUp.call(this, target, coordinates, pointerId, buttonIndex);
             };
             // Statics
             Button.CreateImageButton = function (name, text, imageUrl) {
@@ -4349,8 +4362,8 @@ var BABYLON;
                 }
                 return false;
             };
-            ColorPicker.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
-                if (!_super.prototype._onPointerDown.call(this, target, coordinates, buttonIndex)) {
+            ColorPicker.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
+                if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                     return false;
                 }
                 this._pointerIsDown = true;
@@ -4363,7 +4376,7 @@ var BABYLON;
                     this._pointerStartedOnWheel = true;
                 }
                 this._updateValueFromPointer(coordinates.x, coordinates.y);
-                this._host._capturingControl = this;
+                this._host._capturingControl[pointerId] = this;
                 return true;
             };
             ColorPicker.prototype._onPointerMove = function (target, coordinates) {
@@ -4372,10 +4385,10 @@ var BABYLON;
                 }
                 _super.prototype._onPointerMove.call(this, target, coordinates);
             };
-            ColorPicker.prototype._onPointerUp = function (target, coordinates, buttonIndex) {
+            ColorPicker.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex) {
                 this._pointerIsDown = false;
-                this._host._capturingControl = null;
-                _super.prototype._onPointerUp.call(this, target, coordinates, buttonIndex);
+                delete this._host._capturingControl[pointerId];
+                _super.prototype._onPointerUp.call(this, target, coordinates, pointerId, buttonIndex);
             };
             return ColorPicker;
         }(GUI.Control));
@@ -4797,8 +4810,8 @@ var BABYLON;
                 }
                 context.restore();
             };
-            InputText.prototype._onPointerDown = function (target, coordinates, buttonIndex) {
-                if (!_super.prototype._onPointerDown.call(this, target, coordinates, buttonIndex)) {
+            InputText.prototype._onPointerDown = function (target, coordinates, pointerId, buttonIndex) {
+                if (!_super.prototype._onPointerDown.call(this, target, coordinates, pointerId, buttonIndex)) {
                     return false;
                 }
                 this._clickedCoordinate = coordinates.x;
@@ -4811,8 +4824,8 @@ var BABYLON;
                 this._host.focusedControl = this;
                 return true;
             };
-            InputText.prototype._onPointerUp = function (target, coordinates, buttonIndex) {
-                _super.prototype._onPointerUp.call(this, target, coordinates, buttonIndex);
+            InputText.prototype._onPointerUp = function (target, coordinates, pointerId, buttonIndex) {
+                _super.prototype._onPointerUp.call(this, target, coordinates, pointerId, buttonIndex);
             };
             InputText.prototype.dispose = function () {
                 _super.prototype.dispose.call(this);

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


+ 22 - 19
dist/preview release/gui/babylon.gui.module.d.ts

@@ -24,7 +24,9 @@ declare module BABYLON.GUI {
         _lastPickedControl: Control;
         _lastControlOver: Nullable<Control>;
         _lastControlDown: Nullable<Control>;
-        _capturingControl: Nullable<Control>;
+        _capturingControl: {
+            [pointerId: number]: Control;
+        };
         _shouldBlockPointer: boolean;
         _layerToDispose: Nullable<Layer>;
         _linkedControls: Control[];
@@ -55,7 +57,7 @@ declare module BABYLON.GUI {
         _getGlobalViewport(scene: Scene): Viewport;
         private _checkUpdate(camera);
         private _render();
-        private _doPicking(x, y, type, buttonIndex);
+        private _doPicking(x, y, type, pointerId, buttonIndex);
         attach(): void;
         attachToMesh(mesh: AbstractMesh, supportPointerMove?: boolean): void;
         moveFocusToControl(control: IFocusableControl): void;
@@ -195,6 +197,7 @@ declare module BABYLON.GUI {
         private _downCount;
         private _enterCount;
         private _doNotRender;
+        private _downPointerIds;
         isHitTestVisible: boolean;
         isPointerBlocker: boolean;
         isFocusInvisible: boolean;
@@ -305,14 +308,14 @@ declare module BABYLON.GUI {
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         contains(x: number, y: number): boolean;
-        _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean;
+        _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         _onPointerMove(target: Control, coordinates: Vector2): void;
         _onPointerEnter(target: Control): boolean;
         _onPointerOut(target: Control): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
-        forcePointerUp(): void;
-        _processObservables(type: number, x: number, y: number, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
+        forcePointerUp(pointerId?: Nullable<number>): void;
+        _processObservables(type: number, x: number, y: number, pointerId: number, buttonIndex: number): boolean;
         private _prepareFont();
         dispose(): void;
         private static _HORIZONTAL_ALIGNMENT_LEFT;
@@ -363,7 +366,7 @@ declare module BABYLON.GUI {
         protected _localDraw(context: CanvasRenderingContext2D): void;
         _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean;
+        _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         protected _clipForChildren(context: CanvasRenderingContext2D): void;
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         dispose(): void;
@@ -493,9 +496,9 @@ declare module BABYLON.GUI {
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         private _pointerIsDown;
         private _updateValueFromPointer(x, y);
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
         _onPointerMove(target: Control, coordinates: Vector2): void;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
     }
 }
 
@@ -515,7 +518,7 @@ declare module BABYLON.GUI {
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
     }
 }
 
@@ -536,7 +539,7 @@ declare module BABYLON.GUI {
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
     }
 }
 
@@ -705,11 +708,11 @@ declare module BABYLON.GUI {
         pointerUpAnimation: () => void;
         constructor(name?: string | undefined);
         protected _getTypeName(): string;
-        _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean;
+        _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean;
         _onPointerEnter(target: Control): boolean;
         _onPointerOut(target: Control): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
         static CreateImageButton(name: string, text: string, imageUrl: string): Button;
         static CreateImageOnlyButton(name: string, imageUrl: string): Button;
         static CreateSimpleButton(name: string, text: string): Button;
@@ -750,9 +753,9 @@ declare module BABYLON.GUI {
         private _updateValueFromPointer(x, y);
         private _isPointOnSquare(coordinates);
         private _isPointOnWheel(coordinates);
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
         _onPointerMove(target: Control, coordinates: Vector2): void;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
     }
 }
 
@@ -799,8 +802,8 @@ declare module BABYLON.GUI {
         processKey(keyCode: number, key?: string): void;
         processKeyboard(evt: KeyboardEvent): void;
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
-        _onPointerDown(target: Control, coordinates: Vector2, buttonIndex: number): boolean;
-        _onPointerUp(target: Control, coordinates: Vector2, buttonIndex: number): void;
+        _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean;
+        _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): void;
         dispose(): void;
     }
 }

+ 14 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -331,7 +331,7 @@ declare module BABYLON.GLTF2 {
         private _loadAnimationChannelAsync(context, animationContext, animation, channel, babylonAnimationGroup);
         private _loadAnimationSamplerAsync(context, sampler);
         private _loadBufferAsync(context, buffer);
-        private _loadBufferViewAsync(context, bufferView);
+        _loadBufferViewAsync(context: string, bufferView: ILoaderBufferView): Promise<ArrayBufferView>;
         private _loadAccessorAsync(context, accessor);
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
@@ -368,16 +368,20 @@ declare module BABYLON.GLTF2 {
         protected _loadSceneAsync(context: string, node: ILoaderScene): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading nodes. */
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
+        /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
-        protected _loadExtensionAsync<T>(context: string, property: IProperty, actionAsync: (context: string, extension: T) => Promise<void>): Nullable<Promise<void>>;
+        protected _loadExtensionAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (context: string, extension: TProperty) => Promise<TResult>): Nullable<Promise<TResult>>;
         /** Helper method called by the loader to allow extensions to override loading scenes. */
         static _LoadSceneAsync(loader: GLTFLoader, context: string, scene: ILoaderScene): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading nodes. */
         static _LoadNodeAsync(loader: GLTFLoader, context: string, node: ILoaderNode): Nullable<Promise<void>>;
+        /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+        static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
         static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
@@ -409,6 +413,14 @@ declare module BABYLON.GLTF2.Extensions {
 
 
 declare module BABYLON.GLTF2.Extensions {
+    class KHR_draco_mesh_compression extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;

+ 100 - 6
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -854,11 +854,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
+                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (promise) {
+                    return promise;
+                }
                 var attributes = primitive.attributes;
                 if (!attributes) {
                     throw new Error(context + ": Attributes are missing");
                 }
-                if (primitive.mode && primitive.mode !== 4 /* TRIANGLES */) {
+                if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                     // TODO: handle other primitive modes
                     throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
                 }
@@ -1842,6 +1846,8 @@ var BABYLON;
             GLTFLoaderExtension.prototype._loadSceneAsync = function (context, node) { return null; };
             /** Override this method to modify the default behavior for loading nodes. */
             GLTFLoaderExtension.prototype._loadNodeAsync = function (context, node) { return null; };
+            /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+            GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
             GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
@@ -1849,7 +1855,6 @@ var BABYLON;
             // #endregion
             /** Helper method called by a loader extension to load an glTF extension. */
             GLTFLoaderExtension.prototype._loadExtensionAsync = function (context, property, actionAsync) {
-                var _this = this;
                 if (!property.extensions) {
                     return null;
                 }
@@ -1860,10 +1865,13 @@ var BABYLON;
                 }
                 // Clear out the extension before executing the action to avoid recursing into the same property.
                 delete extensions[this.name];
-                return actionAsync(context + "/extensions/" + this.name, extension).then(function () {
-                    // Restore the extension after completing the action.
-                    extensions[_this.name] = extension;
-                });
+                try {
+                    return actionAsync(context + "/extensions/" + this.name, extension);
+                }
+                finally {
+                    // Restore the extension after executing the action.
+                    extensions[this.name] = extension;
+                }
             };
             /** Helper method called by the loader to allow extensions to override loading scenes. */
             GLTFLoaderExtension._LoadSceneAsync = function (loader, context, scene) {
@@ -1873,6 +1881,10 @@ var BABYLON;
             GLTFLoaderExtension._LoadNodeAsync = function (loader, context, node) {
                 return loader._applyExtensions(function (extension) { return extension._loadNodeAsync(context, node); });
             };
+            /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+            GLTFLoaderExtension._LoadVertexDataAsync = function (loader, context, primitive, babylonMesh) {
+                return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
+            };
             /** Helper method called by the loader to allow extensions to override loading materials. */
             GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
@@ -2056,6 +2068,88 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // https://github.com/KhronosGroup/glTF/pull/874
+            var NAME = "KHR_draco_mesh_compression";
+            var KHR_draco_mesh_compression = /** @class */ (function (_super) {
+                __extends(KHR_draco_mesh_compression, _super);
+                function KHR_draco_mesh_compression() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                KHR_draco_mesh_compression.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, primitive, function (extensionContext, extension) {
+                        if (primitive.mode != undefined) {
+                            if (primitive.mode !== 5 /* TRIANGLE_STRIP */ &&
+                                primitive.mode !== 4 /* TRIANGLES */) {
+                                throw new Error(context + ": Unsupported mode " + primitive.mode);
+                            }
+                            // TODO: handle triangle strips
+                            if (primitive.mode === 5 /* TRIANGLE_STRIP */) {
+                                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                            }
+                        }
+                        var attributes = {};
+                        var loadAttribute = function (name, kind) {
+                            var uniqueId = extension.attributes[name];
+                            if (uniqueId == undefined) {
+                                return;
+                            }
+                            babylonMesh._delayInfo = babylonMesh._delayInfo || [];
+                            if (babylonMesh._delayInfo.indexOf(kind) === -1) {
+                                babylonMesh._delayInfo.push(kind);
+                            }
+                            attributes[kind] = uniqueId;
+                        };
+                        loadAttribute("POSITION", BABYLON.VertexBuffer.PositionKind);
+                        loadAttribute("NORMAL", BABYLON.VertexBuffer.NormalKind);
+                        loadAttribute("TANGENT", BABYLON.VertexBuffer.TangentKind);
+                        loadAttribute("TEXCOORD_0", BABYLON.VertexBuffer.UVKind);
+                        loadAttribute("TEXCOORD_1", BABYLON.VertexBuffer.UV2Kind);
+                        loadAttribute("JOINTS_0", BABYLON.VertexBuffer.MatricesIndicesKind);
+                        loadAttribute("WEIGHTS_0", BABYLON.VertexBuffer.MatricesWeightsKind);
+                        loadAttribute("COLOR_0", BABYLON.VertexBuffer.ColorKind);
+                        var bufferView = GLTF2.GLTFLoader._GetProperty(extensionContext, _this._loader._gltf.bufferViews, extension.bufferView);
+                        return _this._loader._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView).then(function (data) {
+                            try {
+                                return BABYLON.DracoCompression.Decode(data, attributes);
+                            }
+                            catch (e) {
+                                throw new Error(context + ": " + e.message);
+                            }
+                        });
+                    });
+                };
+                return KHR_draco_mesh_compression;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.KHR_draco_mesh_compression = KHR_draco_mesh_compression;
+            if (BABYLON.DracoCompression.IsSupported) {
+                GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_draco_mesh_compression(loader); });
+            }
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=KHR_draco_mesh_compression.js.map
+
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
             // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
             var NAME = "KHR_materials_pbrSpecularGlossiness";
             var KHR_materials_pbrSpecularGlossiness = /** @class */ (function (_super) {

文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 14 - 2
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -886,7 +886,7 @@ declare module BABYLON.GLTF2 {
         private _loadAnimationChannelAsync(context, animationContext, animation, channel, babylonAnimationGroup);
         private _loadAnimationSamplerAsync(context, sampler);
         private _loadBufferAsync(context, buffer);
-        private _loadBufferViewAsync(context, bufferView);
+        _loadBufferViewAsync(context: string, bufferView: ILoaderBufferView): Promise<ArrayBufferView>;
         private _loadAccessorAsync(context, accessor);
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
@@ -923,16 +923,20 @@ declare module BABYLON.GLTF2 {
         protected _loadSceneAsync(context: string, node: ILoaderScene): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading nodes. */
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
+        /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
-        protected _loadExtensionAsync<T>(context: string, property: IProperty, actionAsync: (context: string, extension: T) => Promise<void>): Nullable<Promise<void>>;
+        protected _loadExtensionAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (context: string, extension: TProperty) => Promise<TResult>): Nullable<Promise<TResult>>;
         /** Helper method called by the loader to allow extensions to override loading scenes. */
         static _LoadSceneAsync(loader: GLTFLoader, context: string, scene: ILoaderScene): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading nodes. */
         static _LoadNodeAsync(loader: GLTFLoader, context: string, node: ILoaderNode): Nullable<Promise<void>>;
+        /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+        static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
         static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
@@ -964,6 +968,14 @@ declare module BABYLON.GLTF2.Extensions {
 
 
 declare module BABYLON.GLTF2.Extensions {
+    class KHR_draco_mesh_compression extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;

+ 100 - 6
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3030,11 +3030,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
+                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (promise) {
+                    return promise;
+                }
                 var attributes = primitive.attributes;
                 if (!attributes) {
                     throw new Error(context + ": Attributes are missing");
                 }
-                if (primitive.mode && primitive.mode !== 4 /* TRIANGLES */) {
+                if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                     // TODO: handle other primitive modes
                     throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
                 }
@@ -4018,6 +4022,8 @@ var BABYLON;
             GLTFLoaderExtension.prototype._loadSceneAsync = function (context, node) { return null; };
             /** Override this method to modify the default behavior for loading nodes. */
             GLTFLoaderExtension.prototype._loadNodeAsync = function (context, node) { return null; };
+            /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+            GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
             GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
@@ -4025,7 +4031,6 @@ var BABYLON;
             // #endregion
             /** Helper method called by a loader extension to load an glTF extension. */
             GLTFLoaderExtension.prototype._loadExtensionAsync = function (context, property, actionAsync) {
-                var _this = this;
                 if (!property.extensions) {
                     return null;
                 }
@@ -4036,10 +4041,13 @@ var BABYLON;
                 }
                 // Clear out the extension before executing the action to avoid recursing into the same property.
                 delete extensions[this.name];
-                return actionAsync(context + "/extensions/" + this.name, extension).then(function () {
-                    // Restore the extension after completing the action.
-                    extensions[_this.name] = extension;
-                });
+                try {
+                    return actionAsync(context + "/extensions/" + this.name, extension);
+                }
+                finally {
+                    // Restore the extension after executing the action.
+                    extensions[this.name] = extension;
+                }
             };
             /** Helper method called by the loader to allow extensions to override loading scenes. */
             GLTFLoaderExtension._LoadSceneAsync = function (loader, context, scene) {
@@ -4049,6 +4057,10 @@ var BABYLON;
             GLTFLoaderExtension._LoadNodeAsync = function (loader, context, node) {
                 return loader._applyExtensions(function (extension) { return extension._loadNodeAsync(context, node); });
             };
+            /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+            GLTFLoaderExtension._LoadVertexDataAsync = function (loader, context, primitive, babylonMesh) {
+                return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
+            };
             /** Helper method called by the loader to allow extensions to override loading materials. */
             GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
@@ -4232,6 +4244,88 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // https://github.com/KhronosGroup/glTF/pull/874
+            var NAME = "KHR_draco_mesh_compression";
+            var KHR_draco_mesh_compression = /** @class */ (function (_super) {
+                __extends(KHR_draco_mesh_compression, _super);
+                function KHR_draco_mesh_compression() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                KHR_draco_mesh_compression.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, primitive, function (extensionContext, extension) {
+                        if (primitive.mode != undefined) {
+                            if (primitive.mode !== 5 /* TRIANGLE_STRIP */ &&
+                                primitive.mode !== 4 /* TRIANGLES */) {
+                                throw new Error(context + ": Unsupported mode " + primitive.mode);
+                            }
+                            // TODO: handle triangle strips
+                            if (primitive.mode === 5 /* TRIANGLE_STRIP */) {
+                                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                            }
+                        }
+                        var attributes = {};
+                        var loadAttribute = function (name, kind) {
+                            var uniqueId = extension.attributes[name];
+                            if (uniqueId == undefined) {
+                                return;
+                            }
+                            babylonMesh._delayInfo = babylonMesh._delayInfo || [];
+                            if (babylonMesh._delayInfo.indexOf(kind) === -1) {
+                                babylonMesh._delayInfo.push(kind);
+                            }
+                            attributes[kind] = uniqueId;
+                        };
+                        loadAttribute("POSITION", BABYLON.VertexBuffer.PositionKind);
+                        loadAttribute("NORMAL", BABYLON.VertexBuffer.NormalKind);
+                        loadAttribute("TANGENT", BABYLON.VertexBuffer.TangentKind);
+                        loadAttribute("TEXCOORD_0", BABYLON.VertexBuffer.UVKind);
+                        loadAttribute("TEXCOORD_1", BABYLON.VertexBuffer.UV2Kind);
+                        loadAttribute("JOINTS_0", BABYLON.VertexBuffer.MatricesIndicesKind);
+                        loadAttribute("WEIGHTS_0", BABYLON.VertexBuffer.MatricesWeightsKind);
+                        loadAttribute("COLOR_0", BABYLON.VertexBuffer.ColorKind);
+                        var bufferView = GLTF2.GLTFLoader._GetProperty(extensionContext, _this._loader._gltf.bufferViews, extension.bufferView);
+                        return _this._loader._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView).then(function (data) {
+                            try {
+                                return BABYLON.DracoCompression.Decode(data, attributes);
+                            }
+                            catch (e) {
+                                throw new Error(context + ": " + e.message);
+                            }
+                        });
+                    });
+                };
+                return KHR_draco_mesh_compression;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.KHR_draco_mesh_compression = KHR_draco_mesh_compression;
+            if (BABYLON.DracoCompression.IsSupported) {
+                GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_draco_mesh_compression(loader); });
+            }
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=KHR_draco_mesh_compression.js.map
+
+/// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
+var __extends = (this && this.__extends) || (function () {
+    var extendStatics = Object.setPrototypeOf ||
+        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+    return function (d, b) {
+        extendStatics(d, b);
+        function __() { this.constructor = d; }
+        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+    };
+})();
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
             // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
             var NAME = "KHR_materials_pbrSpecularGlossiness";
             var KHR_materials_pbrSpecularGlossiness = /** @class */ (function (_super) {

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 91 - 6
dist/preview release/loaders/babylonjs.loaders.js

@@ -3998,11 +3998,15 @@ var BABYLON;
             };
             GLTFLoader.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
                 var _this = this;
+                var promise = GLTF2.GLTFLoaderExtension._LoadVertexDataAsync(this, context, primitive, babylonMesh);
+                if (promise) {
+                    return promise;
+                }
                 var attributes = primitive.attributes;
                 if (!attributes) {
                     throw new Error(context + ": Attributes are missing");
                 }
-                if (primitive.mode && primitive.mode !== 4 /* TRIANGLES */) {
+                if (primitive.mode != undefined && primitive.mode !== 4 /* TRIANGLES */) {
                     // TODO: handle other primitive modes
                     throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
                 }
@@ -4986,6 +4990,8 @@ var BABYLON;
             GLTFLoaderExtension.prototype._loadSceneAsync = function (context, node) { return null; };
             /** Override this method to modify the default behavior for loading nodes. */
             GLTFLoaderExtension.prototype._loadNodeAsync = function (context, node) { return null; };
+            /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+            GLTFLoaderExtension.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading materials. */
             GLTFLoaderExtension.prototype._loadMaterialAsync = function (context, material, babylonMesh) { return null; };
             /** Override this method to modify the default behavior for loading uris. */
@@ -4993,7 +4999,6 @@ var BABYLON;
             // #endregion
             /** Helper method called by a loader extension to load an glTF extension. */
             GLTFLoaderExtension.prototype._loadExtensionAsync = function (context, property, actionAsync) {
-                var _this = this;
                 if (!property.extensions) {
                     return null;
                 }
@@ -5004,10 +5009,13 @@ var BABYLON;
                 }
                 // Clear out the extension before executing the action to avoid recursing into the same property.
                 delete extensions[this.name];
-                return actionAsync(context + "/extensions/" + this.name, extension).then(function () {
-                    // Restore the extension after completing the action.
-                    extensions[_this.name] = extension;
-                });
+                try {
+                    return actionAsync(context + "/extensions/" + this.name, extension);
+                }
+                finally {
+                    // Restore the extension after executing the action.
+                    extensions[this.name] = extension;
+                }
             };
             /** Helper method called by the loader to allow extensions to override loading scenes. */
             GLTFLoaderExtension._LoadSceneAsync = function (loader, context, scene) {
@@ -5017,6 +5025,10 @@ var BABYLON;
             GLTFLoaderExtension._LoadNodeAsync = function (loader, context, node) {
                 return loader._applyExtensions(function (extension) { return extension._loadNodeAsync(context, node); });
             };
+            /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+            GLTFLoaderExtension._LoadVertexDataAsync = function (loader, context, primitive, babylonMesh) {
+                return loader._applyExtensions(function (extension) { return extension._loadVertexDataAsync(context, primitive, babylonMesh); });
+            };
             /** Helper method called by the loader to allow extensions to override loading materials. */
             GLTFLoaderExtension._LoadMaterialAsync = function (loader, context, material, babylonMesh) {
                 return loader._applyExtensions(function (extension) { return extension._loadMaterialAsync(context, material, babylonMesh); });
@@ -5182,6 +5194,79 @@ var BABYLON;
     (function (GLTF2) {
         var Extensions;
         (function (Extensions) {
+            // https://github.com/KhronosGroup/glTF/pull/874
+            var NAME = "KHR_draco_mesh_compression";
+            var KHR_draco_mesh_compression = /** @class */ (function (_super) {
+                __extends(KHR_draco_mesh_compression, _super);
+                function KHR_draco_mesh_compression() {
+                    var _this = _super !== null && _super.apply(this, arguments) || this;
+                    _this.name = NAME;
+                    return _this;
+                }
+                KHR_draco_mesh_compression.prototype._loadVertexDataAsync = function (context, primitive, babylonMesh) {
+                    var _this = this;
+                    return this._loadExtensionAsync(context, primitive, function (extensionContext, extension) {
+                        if (primitive.mode != undefined) {
+                            if (primitive.mode !== 5 /* TRIANGLE_STRIP */ &&
+                                primitive.mode !== 4 /* TRIANGLES */) {
+                                throw new Error(context + ": Unsupported mode " + primitive.mode);
+                            }
+                            // TODO: handle triangle strips
+                            if (primitive.mode === 5 /* TRIANGLE_STRIP */) {
+                                throw new Error(context + ": Mode " + primitive.mode + " is not currently supported");
+                            }
+                        }
+                        var attributes = {};
+                        var loadAttribute = function (name, kind) {
+                            var uniqueId = extension.attributes[name];
+                            if (uniqueId == undefined) {
+                                return;
+                            }
+                            babylonMesh._delayInfo = babylonMesh._delayInfo || [];
+                            if (babylonMesh._delayInfo.indexOf(kind) === -1) {
+                                babylonMesh._delayInfo.push(kind);
+                            }
+                            attributes[kind] = uniqueId;
+                        };
+                        loadAttribute("POSITION", BABYLON.VertexBuffer.PositionKind);
+                        loadAttribute("NORMAL", BABYLON.VertexBuffer.NormalKind);
+                        loadAttribute("TANGENT", BABYLON.VertexBuffer.TangentKind);
+                        loadAttribute("TEXCOORD_0", BABYLON.VertexBuffer.UVKind);
+                        loadAttribute("TEXCOORD_1", BABYLON.VertexBuffer.UV2Kind);
+                        loadAttribute("JOINTS_0", BABYLON.VertexBuffer.MatricesIndicesKind);
+                        loadAttribute("WEIGHTS_0", BABYLON.VertexBuffer.MatricesWeightsKind);
+                        loadAttribute("COLOR_0", BABYLON.VertexBuffer.ColorKind);
+                        var bufferView = GLTF2.GLTFLoader._GetProperty(extensionContext, _this._loader._gltf.bufferViews, extension.bufferView);
+                        return _this._loader._loadBufferViewAsync("#/bufferViews/" + bufferView._index, bufferView).then(function (data) {
+                            try {
+                                return BABYLON.DracoCompression.Decode(data, attributes);
+                            }
+                            catch (e) {
+                                throw new Error(context + ": " + e.message);
+                            }
+                        });
+                    });
+                };
+                return KHR_draco_mesh_compression;
+            }(GLTF2.GLTFLoaderExtension));
+            Extensions.KHR_draco_mesh_compression = KHR_draco_mesh_compression;
+            if (BABYLON.DracoCompression.IsSupported) {
+                GLTF2.GLTFLoader._Register(NAME, function (loader) { return new KHR_draco_mesh_compression(loader); });
+            }
+        })(Extensions = GLTF2.Extensions || (GLTF2.Extensions = {}));
+    })(GLTF2 = BABYLON.GLTF2 || (BABYLON.GLTF2 = {}));
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=KHR_draco_mesh_compression.js.map
+
+
+
+var BABYLON;
+(function (BABYLON) {
+    var GLTF2;
+    (function (GLTF2) {
+        var Extensions;
+        (function (Extensions) {
             // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
             var NAME = "KHR_materials_pbrSpecularGlossiness";
             var KHR_materials_pbrSpecularGlossiness = /** @class */ (function (_super) {

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 14 - 2
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -987,7 +987,7 @@ declare module BABYLON.GLTF2 {
         private _loadAnimationChannelAsync(context, animationContext, animation, channel, babylonAnimationGroup);
         private _loadAnimationSamplerAsync(context, sampler);
         private _loadBufferAsync(context, buffer);
-        private _loadBufferViewAsync(context, bufferView);
+        _loadBufferViewAsync(context: string, bufferView: ILoaderBufferView): Promise<ArrayBufferView>;
         private _loadAccessorAsync(context, accessor);
         private _buildArrayBuffer<T>(typedArray, data, byteOffset, count, numComponents, byteStride?);
         private _getDefaultMaterial();
@@ -1024,16 +1024,20 @@ declare module BABYLON.GLTF2 {
         protected _loadSceneAsync(context: string, node: ILoaderScene): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading nodes. */
         protected _loadNodeAsync(context: string, node: ILoaderNode): Nullable<Promise<void>>;
+        /** Override this method to modify the default behavior for loading mesh primitive vertex data. */
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Override this method to modify the default behavior for loading materials. */
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         /** Override this method to modify the default behavior for loading uris. */
         protected _loadUriAsync(context: string, uri: string): Nullable<Promise<ArrayBufferView>>;
         /** Helper method called by a loader extension to load an glTF extension. */
-        protected _loadExtensionAsync<T>(context: string, property: IProperty, actionAsync: (context: string, extension: T) => Promise<void>): Nullable<Promise<void>>;
+        protected _loadExtensionAsync<TProperty, TResult = void>(context: string, property: IProperty, actionAsync: (context: string, extension: TProperty) => Promise<TResult>): Nullable<Promise<TResult>>;
         /** Helper method called by the loader to allow extensions to override loading scenes. */
         static _LoadSceneAsync(loader: GLTFLoader, context: string, scene: ILoaderScene): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading nodes. */
         static _LoadNodeAsync(loader: GLTFLoader, context: string, node: ILoaderNode): Nullable<Promise<void>>;
+        /** Helper method called by the loader to allow extensions to override loading mesh primitive vertex data. */
+        static _LoadVertexDataAsync(loader: GLTFLoader, context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
         /** Helper method called by the loader to allow extensions to override loading materials. */
         static _LoadMaterialAsync(loader: GLTFLoader, context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;
         /** Helper method called by the loader to allow extensions to override loading uris. */
@@ -1065,6 +1069,14 @@ declare module BABYLON.GLTF2.Extensions {
 
 
 declare module BABYLON.GLTF2.Extensions {
+    class KHR_draco_mesh_compression extends GLTFLoaderExtension {
+        readonly name: string;
+        protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>>;
+    }
+}
+
+
+declare module BABYLON.GLTF2.Extensions {
     class KHR_materials_pbrSpecularGlossiness extends GLTFLoaderExtension {
         readonly name: string;
         protected _loadMaterialAsync(context: string, material: ILoaderMaterial, babylonMesh: Mesh): Nullable<Promise<void>>;

+ 486 - 0
dist/preview release/viewer/babylon.viewer.d.ts

@@ -0,0 +1,486 @@
+/// <reference path="../babylon.d.ts"/>
+
+declare module BabylonViewer {
+
+    export let disableInit: boolean;
+
+    export interface ITemplateConfiguration {
+        location?: string;
+        html?: string;
+        id?: string;
+        params?: {
+            [key: string]: string | number | boolean | object;
+        };
+        events?: {
+            pointerdown?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerup?: boolean | {
+                [id: string]: boolean;
+            };
+            pointermove?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerover?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerout?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerenter?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerleave?: boolean | {
+                [id: string]: boolean;
+            };
+            pointercancel?: boolean | {
+                [id: string]: boolean;
+            };
+            click?: boolean | {
+                [id: string]: boolean;
+            };
+            dragstart?: boolean | {
+                [id: string]: boolean;
+            };
+            drop?: boolean | {
+                [id: string]: boolean;
+            };
+            [key: string]: boolean | {
+                [id: string]: boolean;
+            } | undefined;
+        };
+    }
+    export interface EventCallback {
+        event: Event;
+        template: Template;
+        selector: string;
+        payload?: any;
+    }
+    class TemplateManager {
+        containerElement: HTMLElement;
+        onInit: BABYLON.Observable<Template>;
+        onLoaded: BABYLON.Observable<Template>;
+        onStateChange: BABYLON.Observable<Template>;
+        onAllLoaded: BABYLON.Observable<TemplateManager>;
+        onEventTriggered: BABYLON.Observable<EventCallback>;
+        eventManager: EventManager;
+        private templates;
+        constructor(containerElement: HTMLElement);
+        initTemplate(templates: {
+            [key: string]: ITemplateConfiguration;
+        }): void;
+        private buildHTMLTree(templates);
+        getCanvas(): HTMLCanvasElement | null;
+        getTemplate(name: string): Template | undefined;
+        private checkLoadedState();
+    }
+
+    class Template {
+        name: string;
+        private _configuration;
+        onInit: BABYLON.Observable<Template>;
+        onLoaded: BABYLON.Observable<Template>;
+        onAppended: BABYLON.Observable<Template>;
+        onStateChange: BABYLON.Observable<Template>;
+        onEventTriggered: BABYLON.Observable<EventCallback>;
+        isLoaded: boolean;
+        isShown: boolean;
+        parent: HTMLElement;
+        initPromise: Promise<Template>;
+        private fragment;
+        constructor(name: string, _configuration: ITemplateConfiguration);
+        readonly configuration: ITemplateConfiguration;
+        getChildElements(): Array<string>;
+        appendTo(parent: HTMLElement): void;
+        show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+        hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+        dispose(): void;
+        private registerEvents();
+    }
+
+    class ViewerManager {
+        private viewers;
+        onViewerAdded: (viewer: AbstractViewer) => void;
+        onViewerAddedObservable: BABYLON.Observable<AbstractViewer>;
+        constructor();
+        addViewer(viewer: AbstractViewer): void;
+        getViewerById(id: string): AbstractViewer;
+        getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
+        getViewerPromiseById(id: string): Promise<AbstractViewer>;
+        private _onViewerAdded(viewer);
+    }
+    export let viewerManager: ViewerManager;
+
+    export const enum CameraBehavior {
+        AUTOROTATION = 0,
+        BOUNCING = 1,
+        FRAMING = 2,
+    }
+
+    export function InitTags(selector?: string): void;
+
+    class EventManager {
+        private templateManager;
+        private callbacksContainer;
+        constructor(templateManager: TemplateManager);
+        registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+        unregisterCallback(templateName: string, callback?: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+        private eventTriggered(data);
+    }
+
+    class PromiseObservable<T> extends BABYLON.Observable<T> {
+        notifyWithPromise(eventData: T, mask?: number, target?: any, currentTarget?: any): Promise<any>;
+    }
+
+    export interface IMapper {
+        map(rawSource: any): ViewerConfiguration;
+    }
+    class MapperManager {
+        private mappers;
+        static DefaultMapper: string;
+        constructor();
+        getMapper(type: string): IMapper;
+        registerMapper(type: string, mapper: IMapper): void;
+    }
+    export let mapperManager: MapperManager;
+
+    class ConfigurationLoader {
+        private configurationCache;
+        constructor();
+        loadConfiguration(initConfig?: ViewerConfiguration): Promise<ViewerConfiguration>;
+        getConfigurationType(type: string): void;
+        private loadFile(url);
+    }
+    export let configurationLoader: ConfigurationLoader;
+
+
+    /////> configuration
+    export interface ViewerConfiguration {
+
+        // configuration version
+        version?: string;
+        extends?: string; // is this configuration extending an existing configuration?
+
+        pageUrl?: string; // will be used for sharing and other fun stuff. This is the page showing the model (not the model's url!)
+
+        configuration?: string | {
+            url?: string;
+            payload?: any;
+            mapper?: string; // json (default), html, yaml, xml, etc'. if not provided, file extension will be used.
+        };
+
+        // names of functions in the window context.
+        observers?: IObserversConfiguration;
+
+        canvasElement?: string; // if there is a need to override the standard implementation - ID of HTMLCanvasElement
+
+        model?: IModelConfiguration | string;
+
+        scene?: ISceneConfiguration;
+        optimizer?: ISceneOptimizerConfiguration | boolean;
+        // at the moment, support only a single camera.
+        camera?: ICameraConfiguration,
+        skybox?: boolean | ISkyboxConfiguration;
+
+        ground?: boolean | IGroundConfiguration;
+        lights?: { [name: string]: boolean | ILightConfiguration },
+        // engine configuration. optional!
+        engine?: {
+            antialiasing?: boolean;
+            disableResize?: boolean;
+            engineOptions?: { [key: string]: any };
+            adaptiveQuality?: boolean;
+        },
+        //templateStructure?: ITemplateStructure,
+        templates?: {
+            main: ITemplateConfiguration,
+            [key: string]: ITemplateConfiguration
+        };
+
+        customShaders?: {
+            shaders?: {
+                [key: string]: string;
+            };
+            includes?: {
+                [key: string]: string;
+            }
+        }
+
+        // features that are being tested.
+        // those features' syntax will change and move out! 
+        // Don't use in production (or be ready to make the changes :) )
+        lab?: {
+            flashlight?: boolean | {
+                exponent?: number;
+                angle?: number;
+                intensity?: number;
+                diffuse?: { r: number, g: number, b: number };
+                specular?: { r: number, g: number, b: number };
+            }
+            hideLoadingDelay?: number;
+        }
+    }
+
+    export interface IModelConfiguration {
+        url?: string;
+        loader?: string; // obj, gltf?
+        position?: { x: number, y: number, z: number };
+        rotation?: { x: number, y: number, z: number, w?: number };
+        scaling?: { x: number, y: number, z: number };
+        parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
+
+        castShadow?: boolean;
+        normalize?: boolean | {
+            center?: boolean;
+            unitSize?: boolean;
+            parentIndex?: number;
+        }; // shoud the model be scaled to unit-size
+
+        title?: string;
+        subtitle?: string;
+        thumbnail?: string; // URL or data-url
+
+        // [propName: string]: any; // further configuration, like title and creator
+    }
+
+    export interface ISkyboxConfiguration {
+        cubeTexture?: {
+            noMipMap?: boolean;
+            gammaSpace?: boolean;
+            url?: string | Array<string>;
+        };
+        color?: { r: number, g: number, b: number };
+        pbr?: boolean; // deprecated
+        scale?: number;
+        blur?: number; // deprecated
+        material?: {
+            imageProcessingConfiguration?: IImageProcessingConfiguration;
+            [propName: string]: any;
+        };
+        infiniteDIstance?: boolean;
+
+    }
+
+    export interface IGroundConfiguration {
+        size?: number;
+        receiveShadows?: boolean;
+        shadowLevel?: number;
+        shadowOnly?: boolean; // deprecated
+        mirror?: boolean | {
+            sizeRatio?: number;
+            blurKernel?: number;
+            amount?: number;
+            fresnelWeight?: number;
+            fallOffDistance?: number;
+            textureType?: number;
+        };
+        texture?: string;
+        color?: { r: number, g: number, b: number };
+        opacity?: number;
+        material?: { // deprecated!
+            [propName: string]: any;
+        };
+    }
+
+    export interface ISceneConfiguration {
+        debug?: boolean;
+        autoRotate?: boolean; // deprecated
+        rotationSpeed?: number; // deprecated
+        defaultCamera?: boolean; // deprecated
+        defaultLight?: boolean; // deprecated
+        clearColor?: { r: number, g: number, b: number, a: number };
+        imageProcessingConfiguration?: IImageProcessingConfiguration;
+        environmentTexture?: string;
+    }
+
+    export interface ISceneOptimizerConfiguration {
+        targetFrameRate?: number;
+        trackerDuration?: number;
+        autoGeneratePriorities?: boolean;
+        improvementMode?: boolean;
+        degradation?: string; // low, moderate, high
+        types?: {
+            texture?: ISceneOptimizerParameters;
+            hardwareScaling?: ISceneOptimizerParameters;
+            shadow?: ISceneOptimizerParameters;
+            postProcess?: ISceneOptimizerParameters;
+            lensFlare?: ISceneOptimizerParameters;
+            particles?: ISceneOptimizerParameters;
+            renderTarget?: ISceneOptimizerParameters;
+            mergeMeshes?: ISceneOptimizerParameters;
+        }
+    }
+
+    export interface IObserversConfiguration {
+        onEngineInit?: string;
+        onSceneInit?: string;
+        onModelLoaded?: string;
+    }
+
+    export interface ICameraConfiguration {
+        position?: { x: number, y: number, z: number };
+        rotation?: { x: number, y: number, z: number, w: number };
+        fov?: number;
+        fovMode?: number;
+        minZ?: number;
+        maxZ?: number;
+        inertia?: number;
+        behaviors?: {
+            [name: string]: number | {
+                type: number;
+                [propName: string]: any;
+            };
+        };
+
+        [propName: string]: any;
+    }
+
+    export interface ILightConfiguration {
+        type: number;
+        name?: string;
+        disabled?: boolean;
+        position?: { x: number, y: number, z: number };
+        target?: { x: number, y: number, z: number };
+        direction?: { x: number, y: number, z: number };
+        diffuse?: { r: number, g: number, b: number };
+        specular?: { r: number, g: number, b: number };
+        intensity?: number;
+        intensityMode?: number;
+        radius?: number;
+        shadownEnabled?: boolean; // only on specific lights!
+        shadowConfig?: {
+            useBlurExponentialShadowMap?: boolean;
+            useKernelBlur?: boolean;
+            blurKernel?: number;
+            blurScale?: number;
+            minZ?: number;
+            maxZ?: number;
+            frustumSize?: number;
+            angleScale?: number;
+            [propName: string]: any;
+        }
+        [propName: string]: any;
+
+        // no behaviors for light at the moment, but allowing configuration for future reference.
+        behaviors?: {
+            [name: string]: number | {
+                type: number;
+                [propName: string]: any;
+            };
+        };
+    }
+
+    export interface ISceneOptimizerParameters {
+        priority?: number;
+        maximumSize?: number;
+        step?: number;
+    }
+
+    export interface IImageProcessingConfiguration {
+        colorGradingEnabled?: boolean;
+        colorCurvesEnabled?: boolean;
+        colorCurves?: {
+            globalHue?: number;
+            globalDensity?: number;
+            globalSaturation?: number;
+            globalExposure?: number;
+            highlightsHue?: number;
+            highlightsDensity?: number;
+            highlightsSaturation?: number;
+            highlightsExposure?: number;
+            midtonesHue?: number;
+            midtonesDensity?: number;
+            midtonesSaturation?: number;
+            midtonesExposure?: number;
+            shadowsHue?: number;
+            shadowsDensity?: number;
+            shadowsSaturation?: number;
+            shadowsExposure?: number;
+        };
+        colorGradingWithGreenDepth?: boolean;
+        colorGradingBGR?: boolean;
+        exposure?: number;
+        toneMappingEnabled?: boolean;
+        contrast?: number;
+        vignetteEnabled?: boolean;
+        vignetteStretch?: number;
+        vignetteCentreX?: number;
+        vignetteCentreY?: number;
+        vignetteWeight?: number;
+        vignetteColor?: { r: number, g: number, b: number, a?: number };
+        vignetteCameraFov?: number;
+        vignetteBlendMode?: number;
+        vignetteM?: boolean;
+        applyByPostProcess?: boolean;
+
+    }
+    /////>configuration
+
+    /////<viewer
+    export abstract class AbstractViewer {
+        containerElement: HTMLElement;
+        templateManager: TemplateManager;
+        camera: BABYLON.ArcRotateCamera;
+        engine: BABYLON.Engine;
+        scene: BABYLON.Scene;
+        baseId: string;
+        canvas: HTMLCanvasElement;
+        protected configuration: ViewerConfiguration;
+        environmentHelper: BABYLON.EnvironmentHelper;
+        protected defaultHighpTextureType: number;
+        protected shadowGeneratorBias: number;
+        protected defaultPipelineTextureType: number;
+        protected maxShadows: number;
+        onSceneInitObservable: BABYLON.Observable<BABYLON.Scene>;
+        onEngineInitObservable: BABYLON.Observable<BABYLON.Engine>;
+        onModelLoadedObservable: BABYLON.Observable<BABYLON.AbstractMesh[]>;
+        onModelLoadProgressObservable: BABYLON.Observable<BABYLON.SceneLoaderProgressEvent>;
+        onModelLoadErrorObservable: BABYLON.Observable<{ message: string; exception: any }>;
+        onLoaderInitObservable: BABYLON.Observable<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>;
+        onInitDoneObservable: BABYLON.Observable<AbstractViewer>;
+        constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
+        getBaseId(): string;
+        protected abstract prepareContainerElement(): any;
+        protected onTemplatesLoaded(): Promise<AbstractViewer>;
+        protected initEngine(): Promise<BABYLON.Engine>;
+        protected initScene(): Promise<BABYLON.Scene>;
+        dispose(): void;
+        loadModel(model?: any, clearScene?: boolean): Promise<BABYLON.Scene>;
+        lastUsedLoader: BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync;
+        sceneOptimizer: BABYLON.SceneOptimizer;
+        protected registeredOnBeforerenderFunctions: Array<() => void>;
+        isCanvasInDOM(): boolean;
+        protected resize: () => void;
+        protected render: () => void;
+        updateConfiguration(newConfiguration: Partial<ViewerConfiguration>): void;
+        protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean): void;
+        protected configureScene(sceneConfig: ISceneConfiguration, optimizerConfig?: ISceneOptimizerConfiguration): void;
+        protected configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean): void;
+        protected configureObservers(observersConfiguration: IObserversConfiguration): void;
+        protected configureCamera(cameraConfig: ICameraConfiguration, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean }, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        dispose(): void;
+        protected initEnvironment(focusMeshes: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
+        protected injectCustomShaders(): void;
+        protected extendClassWithConfig(object: any, config: any): void;
+        protected handleHardwareLimitations(): void;
+
+
+    }
+
+    export class DefaultViewer extends AbstractViewer {
+        containerElement: HTMLElement;
+        camera: BABYLON.ArcRotateCamera;
+        constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
+        initScene(): Promise<BABYLON.Scene>;
+        protected onTemplatesLoaded(): Promise<AbstractViewer>;
+        protected prepareContainerElement(): void;
+        loadModel(model?: any): Promise<BABYLON.Scene>;
+        initEnvironment(focusMeshes?: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
+        showOverlayScreen(subScreen: string): Promise<Template>;
+        hideOverlayScreen(): Promise<Template>;
+        showLoadingScreen(): Promise<Template>;
+        hideLoadingScreen(): Promise<Template>;
+    }
+}

文件差異過大導致無法顯示
+ 58 - 58
dist/preview release/viewer/babylon.viewer.js


文件差異過大導致無法顯示
+ 110499 - 0
dist/preview release/viewer/babylon.viewer.max.js


+ 486 - 0
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -0,0 +1,486 @@
+/// <reference types="babylonjs"/>
+
+declare module BabylonViewer {
+
+    export let disableInit: boolean;
+
+    export interface ITemplateConfiguration {
+        location?: string;
+        html?: string;
+        id?: string;
+        params?: {
+            [key: string]: string | number | boolean | object;
+        };
+        events?: {
+            pointerdown?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerup?: boolean | {
+                [id: string]: boolean;
+            };
+            pointermove?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerover?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerout?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerenter?: boolean | {
+                [id: string]: boolean;
+            };
+            pointerleave?: boolean | {
+                [id: string]: boolean;
+            };
+            pointercancel?: boolean | {
+                [id: string]: boolean;
+            };
+            click?: boolean | {
+                [id: string]: boolean;
+            };
+            dragstart?: boolean | {
+                [id: string]: boolean;
+            };
+            drop?: boolean | {
+                [id: string]: boolean;
+            };
+            [key: string]: boolean | {
+                [id: string]: boolean;
+            } | undefined;
+        };
+    }
+    export interface EventCallback {
+        event: Event;
+        template: Template;
+        selector: string;
+        payload?: any;
+    }
+    class TemplateManager {
+        containerElement: HTMLElement;
+        onInit: BABYLON.Observable<Template>;
+        onLoaded: BABYLON.Observable<Template>;
+        onStateChange: BABYLON.Observable<Template>;
+        onAllLoaded: BABYLON.Observable<TemplateManager>;
+        onEventTriggered: BABYLON.Observable<EventCallback>;
+        eventManager: EventManager;
+        private templates;
+        constructor(containerElement: HTMLElement);
+        initTemplate(templates: {
+            [key: string]: ITemplateConfiguration;
+        }): void;
+        private buildHTMLTree(templates);
+        getCanvas(): HTMLCanvasElement | null;
+        getTemplate(name: string): Template | undefined;
+        private checkLoadedState();
+    }
+
+    class Template {
+        name: string;
+        private _configuration;
+        onInit: BABYLON.Observable<Template>;
+        onLoaded: BABYLON.Observable<Template>;
+        onAppended: BABYLON.Observable<Template>;
+        onStateChange: BABYLON.Observable<Template>;
+        onEventTriggered: BABYLON.Observable<EventCallback>;
+        isLoaded: boolean;
+        isShown: boolean;
+        parent: HTMLElement;
+        initPromise: Promise<Template>;
+        private fragment;
+        constructor(name: string, _configuration: ITemplateConfiguration);
+        readonly configuration: ITemplateConfiguration;
+        getChildElements(): Array<string>;
+        appendTo(parent: HTMLElement): void;
+        show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+        hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
+        dispose(): void;
+        private registerEvents();
+    }
+
+    class ViewerManager {
+        private viewers;
+        onViewerAdded: (viewer: AbstractViewer) => void;
+        onViewerAddedObservable: BABYLON.Observable<AbstractViewer>;
+        constructor();
+        addViewer(viewer: AbstractViewer): void;
+        getViewerById(id: string): AbstractViewer;
+        getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
+        getViewerPromiseById(id: string): Promise<AbstractViewer>;
+        private _onViewerAdded(viewer);
+    }
+    export let viewerManager: ViewerManager;
+
+    export const enum CameraBehavior {
+        AUTOROTATION = 0,
+        BOUNCING = 1,
+        FRAMING = 2,
+    }
+
+    export function InitTags(selector?: string): void;
+
+    class EventManager {
+        private templateManager;
+        private callbacksContainer;
+        constructor(templateManager: TemplateManager);
+        registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+        unregisterCallback(templateName: string, callback?: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
+        private eventTriggered(data);
+    }
+
+    class PromiseObservable<T> extends BABYLON.Observable<T> {
+        notifyWithPromise(eventData: T, mask?: number, target?: any, currentTarget?: any): Promise<any>;
+    }
+
+    export interface IMapper {
+        map(rawSource: any): ViewerConfiguration;
+    }
+    class MapperManager {
+        private mappers;
+        static DefaultMapper: string;
+        constructor();
+        getMapper(type: string): IMapper;
+        registerMapper(type: string, mapper: IMapper): void;
+    }
+    export let mapperManager: MapperManager;
+
+    class ConfigurationLoader {
+        private configurationCache;
+        constructor();
+        loadConfiguration(initConfig?: ViewerConfiguration): Promise<ViewerConfiguration>;
+        getConfigurationType(type: string): void;
+        private loadFile(url);
+    }
+    export let configurationLoader: ConfigurationLoader;
+
+
+    /////> configuration
+    export interface ViewerConfiguration {
+
+        // configuration version
+        version?: string;
+        extends?: string; // is this configuration extending an existing configuration?
+
+        pageUrl?: string; // will be used for sharing and other fun stuff. This is the page showing the model (not the model's url!)
+
+        configuration?: string | {
+            url?: string;
+            payload?: any;
+            mapper?: string; // json (default), html, yaml, xml, etc'. if not provided, file extension will be used.
+        };
+
+        // names of functions in the window context.
+        observers?: IObserversConfiguration;
+
+        canvasElement?: string; // if there is a need to override the standard implementation - ID of HTMLCanvasElement
+
+        model?: IModelConfiguration | string;
+
+        scene?: ISceneConfiguration;
+        optimizer?: ISceneOptimizerConfiguration | boolean;
+        // at the moment, support only a single camera.
+        camera?: ICameraConfiguration,
+        skybox?: boolean | ISkyboxConfiguration;
+
+        ground?: boolean | IGroundConfiguration;
+        lights?: { [name: string]: boolean | ILightConfiguration },
+        // engine configuration. optional!
+        engine?: {
+            antialiasing?: boolean;
+            disableResize?: boolean;
+            engineOptions?: { [key: string]: any };
+            adaptiveQuality?: boolean;
+        },
+        //templateStructure?: ITemplateStructure,
+        templates?: {
+            main: ITemplateConfiguration,
+            [key: string]: ITemplateConfiguration
+        };
+
+        customShaders?: {
+            shaders?: {
+                [key: string]: string;
+            };
+            includes?: {
+                [key: string]: string;
+            }
+        }
+
+        // features that are being tested.
+        // those features' syntax will change and move out! 
+        // Don't use in production (or be ready to make the changes :) )
+        lab?: {
+            flashlight?: boolean | {
+                exponent?: number;
+                angle?: number;
+                intensity?: number;
+                diffuse?: { r: number, g: number, b: number };
+                specular?: { r: number, g: number, b: number };
+            }
+            hideLoadingDelay?: number;
+        }
+    }
+
+    export interface IModelConfiguration {
+        url?: string;
+        loader?: string; // obj, gltf?
+        position?: { x: number, y: number, z: number };
+        rotation?: { x: number, y: number, z: number, w?: number };
+        scaling?: { x: number, y: number, z: number };
+        parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
+
+        castShadow?: boolean;
+        normalize?: boolean | {
+            center?: boolean;
+            unitSize?: boolean;
+            parentIndex?: number;
+        }; // shoud the model be scaled to unit-size
+
+        title?: string;
+        subtitle?: string;
+        thumbnail?: string; // URL or data-url
+
+        // [propName: string]: any; // further configuration, like title and creator
+    }
+
+    export interface ISkyboxConfiguration {
+        cubeTexture?: {
+            noMipMap?: boolean;
+            gammaSpace?: boolean;
+            url?: string | Array<string>;
+        };
+        color?: { r: number, g: number, b: number };
+        pbr?: boolean; // deprecated
+        scale?: number;
+        blur?: number; // deprecated
+        material?: {
+            imageProcessingConfiguration?: IImageProcessingConfiguration;
+            [propName: string]: any;
+        };
+        infiniteDIstance?: boolean;
+
+    }
+
+    export interface IGroundConfiguration {
+        size?: number;
+        receiveShadows?: boolean;
+        shadowLevel?: number;
+        shadowOnly?: boolean; // deprecated
+        mirror?: boolean | {
+            sizeRatio?: number;
+            blurKernel?: number;
+            amount?: number;
+            fresnelWeight?: number;
+            fallOffDistance?: number;
+            textureType?: number;
+        };
+        texture?: string;
+        color?: { r: number, g: number, b: number };
+        opacity?: number;
+        material?: { // deprecated!
+            [propName: string]: any;
+        };
+    }
+
+    export interface ISceneConfiguration {
+        debug?: boolean;
+        autoRotate?: boolean; // deprecated
+        rotationSpeed?: number; // deprecated
+        defaultCamera?: boolean; // deprecated
+        defaultLight?: boolean; // deprecated
+        clearColor?: { r: number, g: number, b: number, a: number };
+        imageProcessingConfiguration?: IImageProcessingConfiguration;
+        environmentTexture?: string;
+    }
+
+    export interface ISceneOptimizerConfiguration {
+        targetFrameRate?: number;
+        trackerDuration?: number;
+        autoGeneratePriorities?: boolean;
+        improvementMode?: boolean;
+        degradation?: string; // low, moderate, high
+        types?: {
+            texture?: ISceneOptimizerParameters;
+            hardwareScaling?: ISceneOptimizerParameters;
+            shadow?: ISceneOptimizerParameters;
+            postProcess?: ISceneOptimizerParameters;
+            lensFlare?: ISceneOptimizerParameters;
+            particles?: ISceneOptimizerParameters;
+            renderTarget?: ISceneOptimizerParameters;
+            mergeMeshes?: ISceneOptimizerParameters;
+        }
+    }
+
+    export interface IObserversConfiguration {
+        onEngineInit?: string;
+        onSceneInit?: string;
+        onModelLoaded?: string;
+    }
+
+    export interface ICameraConfiguration {
+        position?: { x: number, y: number, z: number };
+        rotation?: { x: number, y: number, z: number, w: number };
+        fov?: number;
+        fovMode?: number;
+        minZ?: number;
+        maxZ?: number;
+        inertia?: number;
+        behaviors?: {
+            [name: string]: number | {
+                type: number;
+                [propName: string]: any;
+            };
+        };
+
+        [propName: string]: any;
+    }
+
+    export interface ILightConfiguration {
+        type: number;
+        name?: string;
+        disabled?: boolean;
+        position?: { x: number, y: number, z: number };
+        target?: { x: number, y: number, z: number };
+        direction?: { x: number, y: number, z: number };
+        diffuse?: { r: number, g: number, b: number };
+        specular?: { r: number, g: number, b: number };
+        intensity?: number;
+        intensityMode?: number;
+        radius?: number;
+        shadownEnabled?: boolean; // only on specific lights!
+        shadowConfig?: {
+            useBlurExponentialShadowMap?: boolean;
+            useKernelBlur?: boolean;
+            blurKernel?: number;
+            blurScale?: number;
+            minZ?: number;
+            maxZ?: number;
+            frustumSize?: number;
+            angleScale?: number;
+            [propName: string]: any;
+        }
+        [propName: string]: any;
+
+        // no behaviors for light at the moment, but allowing configuration for future reference.
+        behaviors?: {
+            [name: string]: number | {
+                type: number;
+                [propName: string]: any;
+            };
+        };
+    }
+
+    export interface ISceneOptimizerParameters {
+        priority?: number;
+        maximumSize?: number;
+        step?: number;
+    }
+
+    export interface IImageProcessingConfiguration {
+        colorGradingEnabled?: boolean;
+        colorCurvesEnabled?: boolean;
+        colorCurves?: {
+            globalHue?: number;
+            globalDensity?: number;
+            globalSaturation?: number;
+            globalExposure?: number;
+            highlightsHue?: number;
+            highlightsDensity?: number;
+            highlightsSaturation?: number;
+            highlightsExposure?: number;
+            midtonesHue?: number;
+            midtonesDensity?: number;
+            midtonesSaturation?: number;
+            midtonesExposure?: number;
+            shadowsHue?: number;
+            shadowsDensity?: number;
+            shadowsSaturation?: number;
+            shadowsExposure?: number;
+        };
+        colorGradingWithGreenDepth?: boolean;
+        colorGradingBGR?: boolean;
+        exposure?: number;
+        toneMappingEnabled?: boolean;
+        contrast?: number;
+        vignetteEnabled?: boolean;
+        vignetteStretch?: number;
+        vignetteCentreX?: number;
+        vignetteCentreY?: number;
+        vignetteWeight?: number;
+        vignetteColor?: { r: number, g: number, b: number, a?: number };
+        vignetteCameraFov?: number;
+        vignetteBlendMode?: number;
+        vignetteM?: boolean;
+        applyByPostProcess?: boolean;
+
+    }
+    /////>configuration
+
+    /////<viewer
+    export abstract class AbstractViewer {
+        containerElement: HTMLElement;
+        templateManager: TemplateManager;
+        camera: BABYLON.ArcRotateCamera;
+        engine: BABYLON.Engine;
+        scene: BABYLON.Scene;
+        baseId: string;
+        canvas: HTMLCanvasElement;
+        protected configuration: ViewerConfiguration;
+        environmentHelper: BABYLON.EnvironmentHelper;
+        protected defaultHighpTextureType: number;
+        protected shadowGeneratorBias: number;
+        protected defaultPipelineTextureType: number;
+        protected maxShadows: number;
+        onSceneInitObservable: BABYLON.Observable<BABYLON.Scene>;
+        onEngineInitObservable: BABYLON.Observable<BABYLON.Engine>;
+        onModelLoadedObservable: BABYLON.Observable<BABYLON.AbstractMesh[]>;
+        onModelLoadProgressObservable: BABYLON.Observable<BABYLON.SceneLoaderProgressEvent>;
+        onModelLoadErrorObservable: BABYLON.Observable<{ message: string; exception: any }>;
+        onLoaderInitObservable: BABYLON.Observable<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>;
+        onInitDoneObservable: BABYLON.Observable<AbstractViewer>;
+        constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
+        getBaseId(): string;
+        protected abstract prepareContainerElement(): any;
+        protected onTemplatesLoaded(): Promise<AbstractViewer>;
+        protected initEngine(): Promise<BABYLON.Engine>;
+        protected initScene(): Promise<BABYLON.Scene>;
+        dispose(): void;
+        loadModel(model?: any, clearScene?: boolean): Promise<BABYLON.Scene>;
+        lastUsedLoader: BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync;
+        sceneOptimizer: BABYLON.SceneOptimizer;
+        protected registeredOnBeforerenderFunctions: Array<() => void>;
+        isCanvasInDOM(): boolean;
+        protected resize: () => void;
+        protected render: () => void;
+        updateConfiguration(newConfiguration: Partial<ViewerConfiguration>): void;
+        protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean): void;
+        protected configureScene(sceneConfig: ISceneConfiguration, optimizerConfig?: ISceneOptimizerConfiguration): void;
+        protected configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean): void;
+        protected configureObservers(observersConfiguration: IObserversConfiguration): void;
+        protected configureCamera(cameraConfig: ICameraConfiguration, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean }, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<BABYLON.AbstractMesh>): void;
+        dispose(): void;
+        protected initEnvironment(focusMeshes: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
+        protected injectCustomShaders(): void;
+        protected extendClassWithConfig(object: any, config: any): void;
+        protected handleHardwareLimitations(): void;
+
+
+    }
+
+    export class DefaultViewer extends AbstractViewer {
+        containerElement: HTMLElement;
+        camera: BABYLON.ArcRotateCamera;
+        constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
+        initScene(): Promise<BABYLON.Scene>;
+        protected onTemplatesLoaded(): Promise<AbstractViewer>;
+        protected prepareContainerElement(): void;
+        loadModel(model?: any): Promise<BABYLON.Scene>;
+        initEnvironment(focusMeshes?: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
+        showOverlayScreen(subScreen: string): Promise<Template>;
+        hideOverlayScreen(): Promise<Template>;
+        showLoadingScreen(): Promise<Template>;
+        hideLoadingScreen(): Promise<Template>;
+    }
+}

+ 2 - 0
dist/preview release/viewer/package.json

@@ -12,9 +12,11 @@
     "main": "babylon.viewer.js",
     "files": [
         "babylon.viewer.js",
+        "babylon.viewer.module.d.ts",
         "readme.md",
         "package.json"
     ],
+    "typings": "babylon.viewer.module.d.ts",
     "keywords": [
         "3D",
         "javascript",

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

@@ -68,9 +68,11 @@
 - Ability to set a mesh to customize the webVR gaze tracker ([trevordev](https://github.com/trevordev))
 - Added promise-based async functions for initWebVRAsync and useStandingMatrixAsync ([trevordev](https://github.com/trevordev))
 - Add stroke (outline) options on GUI text control ([SvenFrankson](https://github.com/SvenFrankson))
+- Add isThumbClamped option on GUI slider control ([JeanPhilippeKernel](https://github.com/JeanPhilippeKernel))
 - Add floating point texture support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 - Support for mutli-touch when interacting with multiple gui elements simultaneously ([trevordev](https://github.com/trevordev))
-- Added Draco mesh compression support to glTF 2.0 loader. ([bghgary](https://github.com/bghgary)) 
+- (Viewer) Declaration file published. ([RaananW](https://github.com/RaananW))
+- Added Draco mesh compression support to glTF 2.0 loader. ([bghgary](https://github.com/bghgary))
 
 ## Bug fixes
 

+ 11 - 2
src/Engine/babylon.engine.ts

@@ -349,6 +349,7 @@
         private static _TEXTUREFORMAT_R32F = 6;
         private static _TEXTUREFORMAT_RG32F = 7;
         private static _TEXTUREFORMAT_RGB32F = 8;
+        private static _TEXTUREFORMAT_RGBA32F = 9;
 
         private static _TEXTURETYPE_UNSIGNED_INT = 0;
         private static _TEXTURETYPE_FLOAT = 1;
@@ -520,7 +521,14 @@
          */
         public static get TEXTUREFORMAT_RGB32F(): number {
             return Engine._TEXTUREFORMAT_RGB32F;
-        }               
+        }       
+        
+        /**
+         * RGBA32F
+         */
+        public static get TEXTUREFORMAT_RGBA32F(): number {
+            return Engine._TEXTUREFORMAT_RGBA32F;
+        }             
 
         public static get TEXTUREFORMAT_LUMINANCE_ALPHA(): number {
             return Engine._TEXTUREFORMAT_LUMINANCE_ALPHA;
@@ -565,7 +573,7 @@
         }
 
         public static get Version(): string {
-            return "3.2.0-alpha7";
+            return "3.2.0-alpha8";
         }
 
         // Updatable statics so stick with vars here
@@ -3411,6 +3419,7 @@
                     internalFormat = this._gl.RGB;
                     break;
                 case Engine.TEXTUREFORMAT_RGBA:
+                case Engine.TEXTUREFORMAT_RGBA32F:
                     internalFormat = this._gl.RGBA;
                     break;
                 case Engine.TEXTUREFORMAT_R32F:

+ 24 - 0
src/Particles/babylon.iParticleEmitterType.ts

@@ -26,5 +26,29 @@ module BABYLON {
          * @returns the new emitter
          */
         clone(): IParticleEmitterType;
+
+        /**
+         * Called by the {BABYLON.GPUParticleSystem} to setup the update shader
+         * @param effect defines the update shader
+         */
+        applyToShader(effect: Effect): void;
+
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns the effect defines string
+         */
+        getEffectDefines(): string;
+
+        /**
+         * Returns a string representing the class name
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        serialize(): any;
     }
 }

+ 44 - 44
src/Particles/babylon.boxParticleEmitter.ts

@@ -8,62 +8,25 @@ module BABYLON {
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          */
-        public get direction1(): Vector3 {
-            return this._particleSystem.direction1;
-        }
+        public direction1 = new Vector3(0, 1.0, 0);
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          */
-        public set direction1(value: Vector3) {
-            this._particleSystem.direction1 = value;
-        }
-
-        /**
-         * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
-         */
-        public get direction2(): Vector3 {
-            return this._particleSystem.direction2;
-        }
-        /**
-         * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
-         */
-        public set direction2(value: Vector3) {
-            this._particleSystem.direction2 = value;
-        }
+        public direction2 = new Vector3(0, 1.0, 0);
 
         /**
          * Minimum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
          */
-        public get minEmitBox(): Vector3 {
-            return this._particleSystem.minEmitBox;
-        }
-        /**
-         * Minimum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
-         */
-        public set minEmitBox(value: Vector3) {
-            this._particleSystem.minEmitBox = value;
-        }
-
+        public minEmitBox = new Vector3(-0.5, -0.5, -0.5);
         /**
          * Maximum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
          */
-        public get maxEmitBox(): Vector3 {
-            return this._particleSystem.maxEmitBox;
-        }
-        /**
-         * Maximum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
-         */
-        public set maxEmitBox(value: Vector3) {
-            this._particleSystem.maxEmitBox = value;
-        }
-        
-        // to be updated like the rest of emitters when breaking changes.
-        // all property should be come public variables and passed through constructor.
+        public maxEmitBox = new Vector3(0.5, 0.5, 0.5);  
+               
         /**
          * Creates a new instance of @see BoxParticleEmitter
-         * @param _particleSystem the particle system associated with the emitter
          */
-        constructor(private _particleSystem: ParticleSystem) {
+        constructor() {
 
         }
 
@@ -102,11 +65,48 @@ module BABYLON {
          */
         public clone(): BoxParticleEmitter
         {
-            let newOne = new BoxParticleEmitter(this._particleSystem);
+            let newOne = new BoxParticleEmitter();
 
             Tools.DeepCopy(this, newOne);
 
             return newOne;
         }
+
+        /**
+         * Called by the {BABYLON.GPUParticleSystem} to setup the update shader
+         * @param effect defines the update shader
+         */        
+        public applyToShader(effect: Effect): void {            
+            effect.setVector3("direction1", this.direction1);
+            effect.setVector3("direction2", this.direction2);
+            effect.setVector3("minEmitBox", this.minEmitBox);
+            effect.setVector3("maxEmitBox", this.maxEmitBox);
+        }
+
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns a string containng the defines string
+         */
+        public getEffectDefines(): string {
+            return "#define BOXEMITTER"
+        }
+
+        /**
+         * Returns the string "BoxEmitter"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "BoxEmitter";
+        }   
+        
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        public serialize(): any {
+            var serializationObject: any = {};
+
+            return serializationObject;
+        }
     }
 }

+ 39 - 2
src/Particles/babylon.coneParticleEmitter.ts

@@ -88,7 +88,7 @@ module BABYLON {
 
             var randX = radius * Math.sin(s);
             var randZ = radius * Math.cos(s);
-            var randY = h;
+            var randY = h * this._height;
 
             Vector3.TransformCoordinatesFromFloatsToRef(randX, randY, randZ, worldMatrix, positionToUpdate);
         }
@@ -103,6 +103,43 @@ module BABYLON {
             Tools.DeepCopy(this, newOne);
 
             return newOne;
-        }          
+        }        
+        
+        /**
+         * Called by the {BABYLON.GPUParticleSystem} to setup the update shader
+         * @param effect defines the update shader
+         */        
+        public applyToShader(effect: Effect): void {
+            effect.setFloat("radius", this.radius);
+            effect.setFloat("angle", this.angle);
+            effect.setFloat("height", this._height);
+            effect.setFloat("directionRandomizer", this.directionRandomizer);
+        }
+
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns a string containng the defines string
+         */
+        public getEffectDefines(): string {
+            return "#define CONEEMITTER"
+        }     
+        
+        /**
+         * Returns the string "BoxEmitter"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "ConeEmitter";
+        }  
+        
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        public serialize(): any {
+            var serializationObject: any = {};
+
+            return serializationObject;
+        }        
     }
 }

+ 72 - 1
src/Particles/babylon.sphereParticleEmitter.ts

@@ -67,6 +67,41 @@ module BABYLON {
             Tools.DeepCopy(this, newOne);
 
             return newOne;
+        }    
+        
+        /**
+         * Called by the {BABYLON.GPUParticleSystem} to setup the update shader
+         * @param effect defines the update shader
+         */        
+        public applyToShader(effect: Effect): void {
+            effect.setFloat("radius", this.radius);
+            effect.setFloat("directionRandomizer", this.directionRandomizer);
+        }    
+        
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns a string containng the defines string
+         */
+        public getEffectDefines(): string {
+            return "#define SPHEREEMITTER"
+        }   
+        
+        /**
+         * Returns the string "SphereParticleEmitter"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "SphereParticleEmitter";
+        }         
+        
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        public serialize(): any {
+            var serializationObject: any = {};
+
+            return serializationObject;
         }        
     }
 
@@ -118,6 +153,42 @@ module BABYLON {
             Tools.DeepCopy(this, newOne);
 
             return newOne;
-        }          
+        }     
+        
+        /**
+         * Called by the {BABYLON.GPUParticleSystem} to setup the update shader
+         * @param effect defines the update shader
+         */        
+        public applyToShader(effect: Effect): void {
+            effect.setFloat("radius", this.radius);
+            effect.setVector3("direction1", this.direction1);
+            effect.setVector3("direction2", this.direction2);
+        }       
+        
+        /**
+         * Returns a string to use to update the GPU particles update shader
+         * @returns a string containng the defines string
+         */
+        public getEffectDefines(): string {
+            return "#define SPHEREEMITTER\n#define DIRECTEDSPHEREEMITTER"
+        }    
+        
+        /**
+         * Returns the string "SphereDirectedParticleEmitter"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "SphereDirectedParticleEmitter";
+        }       
+        
+        /**
+         * Serializes the particle system to a JSON object.
+         * @returns the JSON object
+         */        
+        public serialize(): any {
+            var serializationObject: any = {};
+
+            return serializationObject;
+        }        
     }
 }

+ 213 - 47
src/Particles/babylon.gpuParticleSystem.ts

@@ -3,7 +3,7 @@
      * This represents a GPU particle system in Babylon.
      * This os the fastest particle system in Babylon as it uses the GPU to update the individual particle data.
      */
-    export class GPUParticleSystem implements IDisposable, IParticleSystem {
+    export class GPUParticleSystem implements IDisposable, IParticleSystem, IAnimatable {
         /**
          * The id of the Particle system.
          */
@@ -50,12 +50,32 @@
 
         private _currentRenderId = -1;    
         private _started = false;    
+        private _stopped = false;    
 
         private _timeDelta = 0;
 
         private _randomTexture: RawTexture;
 
         private readonly _attributesStrideSize = 14;
+        private _updateEffectOptions: EffectCreationOptions;
+
+        private _randomTextureSize: number;
+        private _actualFrame = 0;        
+
+        /**
+         * List of animations used by the particle system.
+         */
+        public animations: Animation[] = [];        
+
+        /**
+         * Gets a boolean indicating if the GPU particles can be rendered on current browser
+         */
+        public static get IsSupported(): boolean {
+            if (!Engine.LastCreatedEngine) {
+                return false;
+            }
+            return Engine.LastCreatedEngine.webGLVersion > 1;
+        }
 
         /**
         * An event triggered when the system is disposed.
@@ -68,6 +88,11 @@
         public updateSpeed = 0.01;        
 
         /**
+         * The amount of time the particle system is running (depends of the overall update speed).
+         */
+        public targetStopDuration = 0;        
+
+        /**
          * The texture used to render each particle. (this can be a spritesheet)
          */
         public particleTexture: Nullable<Texture>;   
@@ -120,9 +145,25 @@
         public gravity = Vector3.Zero();    
 
         /**
-         * Gets the maximum number of particles supported by this system
+         * Minimum power of emitting particles.
+         */
+        public minEmitPower = 1;
+        /**
+         * Maximum power of emitting particles.
          */
-        public get capacity(): number {
+        public maxEmitPower = 1;        
+
+        /**
+         * The particle emitter type defines the emitter used by the particle system.
+         * It can be for example box, sphere, or cone...
+         */
+        public particleEmitterType: Nullable<IParticleEmitterType>;        
+
+        /**
+         * Gets the maximum number of particles active at the same time.
+         * @returns The max number of active particles.
+         */
+        public getCapacity(): number {
             return this._capacity;
         }
 
@@ -150,36 +191,66 @@
          */
         public start(): void {
             this._started = true;
+            this._stopped = false;
         }
 
         /**
          * Stops the particle system.
          */
         public stop(): void {
-            this._started = false;
+            this._stopped = true;
         }
 
         /**
+         * Remove all active particles
+         */
+        public reset(): void {
+            this._releaseBuffers();
+            this._releaseVAOs();   
+            this._currentActiveCount = 0;         
+            this._targetIndex = 0;
+        }      
+        
+        /**
+         * Returns the string "GPUParticleSystem"
+         * @returns a string containing the class name 
+         */
+        public getClassName(): string {
+            return "GPUParticleSystem";
+        }            
+
+        /**
          * Instantiates a GPU particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * @param name The name of the particle system
          * @param capacity The max number of particles alive at the same time
          * @param scene The scene the particle system belongs to
          */
-        constructor(name: string, capacity: number, scene: Scene) {
+        constructor(name: string, options: Partial<{
+                        capacity: number,
+                        randomTextureSize: number
+                    }>, scene: Scene) {
             this.id = name;
             this.name = name;
             this._scene = scene || Engine.LastCreatedScene;
-            this._capacity = capacity;
-            this._activeCount = capacity;
-            this._currentActiveCount = 0;
             this._engine = this._scene.getEngine();
 
+            let fullOptions = {
+                capacity: 50000,
+                randomTextureSize: this._engine.getCaps().maxTextureSize,
+                ...options
+            };
+
+            this._capacity = fullOptions.capacity;
+            this._activeCount = fullOptions.capacity;
+            this._currentActiveCount = 0;
+
             this._scene.particleSystems.push(this);
 
-            let updateEffectOptions: EffectCreationOptions = {
+            this._updateEffectOptions = {
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction"],
-                uniformsNames: ["currentCount", "timeDelta", "generalRandom", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity"],
+                uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity", "emitPower",
+                                "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "angle"],
                 uniformBuffersNames: [],
                 samplers:["randomSampler"],
                 defines: "",
@@ -191,33 +262,24 @@
                 transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outSize", "outColor", "outDirection"]
             };
 
-            this._updateEffect = new Effect("gpuUpdateParticles", updateEffectOptions, this._scene.getEngine());   
+            this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());   
 
             this._renderEffect = new Effect("gpuRenderParticles", ["position", "age", "life", "size", "color", "offset", "uv"], ["view", "projection", "colorDead"], ["textureSampler"], this._scene.getEngine());
 
             // Random data
-            var maxTextureSize = this._engine.getCaps().maxTextureSize;
+            var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
             var d = [];
             for (var i = 0; i < maxTextureSize; ++i) {
                 d.push(Math.random());
                 d.push(Math.random());
                 d.push(Math.random());
+                d.push(Math.random());
             }
-            this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGB32F, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT)
+            this._randomTexture = new RawTexture(new Float32Array(d), maxTextureSize, 1, Engine.TEXTUREFORMAT_RGBA32F, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE, Engine.TEXTURETYPE_FLOAT)
             this._randomTexture.wrapU = Texture.WRAP_ADDRESSMODE;
             this._randomTexture.wrapV = Texture.WRAP_ADDRESSMODE;
-        }
-
-        /**
-         * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
-         */
-        public animate(): void {
-            if (this._currentRenderId === this._scene.getRenderId()) {
-                return;
-            }
 
-            this._currentRenderId = this._scene.getRenderId();
-            this._timeDelta = this.updateSpeed * this._scene.getAnimationRatio();               
+            this._randomTextureSize = maxTextureSize;
         }
 
         private _createUpdateVAO(source: Buffer): WebGLVertexArrayObject {            
@@ -253,8 +315,8 @@
             return vao;
         }        
         
-        private _initialize(): void {
-            if (this._buffer0) {
+        private _initialize(force = false): void {
+            if (this._buffer0 && !force) {
                 return;
             }
 
@@ -289,10 +351,10 @@
             }
 
             // Sprite data
-            var spriteData = new Float32Array([1, 1,  1, 1,  
-                                              -1, 1,  0, 1,
-                                             -1, -1,  0, 0,   
-                                              1, -1,  1, 0]);
+            var spriteData = new Float32Array([0.5, 0.5,  1, 1,  
+                                              -0.5, 0.5,  0, 1,
+                                             -0.5, -0.5,  0, 0,   
+                                             0.5, -0.5,  1, 0]);
 
             // Buffers
             this._buffer0 = new Buffer(engine, data, false, this._attributesStrideSize);
@@ -312,34 +374,79 @@
             this._targetBuffer = this._buffer1;
 
         }
+
+        /** @ignore */
+        public _recreateUpdateEffect(defines: string) {
+            if (this._updateEffectOptions.defines === defines) {
+                return;
+            }
+            this._updateEffectOptions.defines = defines;
+            this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());   
+        }
+
+        /**
+         * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
+         */
+        public animate(): void {           
+            if (!this._stopped) {
+                this._timeDelta = this.updateSpeed * this._scene.getAnimationRatio();   
+                this._actualFrame += this._timeDelta;
+
+                if (this.targetStopDuration && this._actualFrame >= this.targetStopDuration)
+                    this.stop();
+            } else {
+                this._timeDelta = 0;
+            }             
+        }        
+
         /**
          * Renders the particle system in its current state.
-         * @returns the current number of particles.
+         * @returns the current number of particles
          */
         public render(): number {
+            if (!this._started) {
+                return 0;
+            }
+
+            if (this.particleEmitterType) {
+                this._recreateUpdateEffect(this.particleEmitterType.getEffectDefines());
+            }
+
             if (!this.emitter || !this._updateEffect.isReady() || !this._renderEffect.isReady() ) {
                 return 0;
             }
 
+            if (this._currentRenderId === this._scene.getRenderId()) {
+                return 0;
+            }
+
+            this._currentRenderId = this._scene.getRenderId();      
+            
             // Get everything ready to render
             this. _initialize();
 
             this._currentActiveCount = Math.min(this._activeCount, this._currentActiveCount + this.emitRate);
             
             // Enable update effect
+
             this._engine.enableEffect(this._updateEffect);
             this._engine.setState(false);    
             
             this._updateEffect.setFloat("currentCount", this._currentActiveCount);
             this._updateEffect.setFloat("timeDelta", this._timeDelta);
-            this._updateEffect.setFloat("generalRandom", Math.random());
+            this._updateEffect.setFloat3("generalRandoms", Math.random(), Math.random(), Math.random());
             this._updateEffect.setTexture("randomSampler", this._randomTexture);
             this._updateEffect.setFloat2("lifeTime", this.minLifeTime, this.maxLifeTime);
+            this._updateEffect.setFloat2("emitPower", this.minEmitPower, this.maxEmitPower);
             this._updateEffect.setDirectColor4("color1", this.color1);
             this._updateEffect.setDirectColor4("color2", this.color2);
             this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
             this._updateEffect.setVector3("gravity", this.gravity);
 
+            if (this.particleEmitterType) {
+                this.particleEmitterType.applyToShader(this._updateEffect);
+            }
+
             let emitterWM: Matrix;
             if ((<AbstractMesh>this.emitter).position) {
                 var emitterMesh = (<AbstractMesh>this.emitter);
@@ -392,27 +499,19 @@
             // Switch buffers
             let tmpBuffer = this._sourceBuffer;
             this._sourceBuffer = this._targetBuffer;
-            this._targetBuffer = tmpBuffer;            
-
-            return 0;
+            this._targetBuffer = tmpBuffer;     
+            
+            return this._currentActiveCount;
         }
 
         /**
          * Rebuilds the particle system
          */
         public rebuild(): void {
-            
+            this._initialize(true);
         }
 
-        /**
-         * Disposes the particle system and free the associated resources.
-         */
-        public dispose(): void {
-            var index = this._scene.particleSystems.indexOf(this);
-            if (index > -1) {
-                this._scene.particleSystems.splice(index, 1);
-            }
-
+        private _releaseBuffers() {
             if (this._buffer0) {
                 this._buffer0.dispose();
                 (<any>this._buffer0) = null;
@@ -421,7 +520,13 @@
                 this._buffer1.dispose();
                 (<any>this._buffer1) = null;
             }
-            
+            if (this._spriteBuffer) {
+                this._spriteBuffer.dispose();
+                (<any>this._spriteBuffer) = null;
+            }            
+        }
+
+        private _releaseVAOs() {
             for (var index = 0; index < this._updateVAO.length; index++) {
                 this._engine.releaseVertexArrayObject(this._updateVAO[index]);
             }
@@ -430,7 +535,21 @@
             for (var index = 0; index < this._renderVAO.length; index++) {
                 this._engine.releaseVertexArrayObject(this._renderVAO[index]);
             }
-            this._renderVAO = [];            
+            this._renderVAO = [];   
+        }
+
+        /**
+         * Disposes the particle system and free the associated resources.
+         */
+        public dispose(): void {
+            var index = this._scene.particleSystems.indexOf(this);
+            if (index > -1) {
+                this._scene.particleSystems.splice(index, 1);
+            }
+
+            this._releaseBuffers();
+            this._releaseVAOs();
+         
 
             if (this._randomTexture) {
                 this._randomTexture.dispose();
@@ -459,6 +578,53 @@
          * @returns the JSON object
          */
         public serialize(): any {
+            var serializationObject: any = {};
+
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+
+            // Emitter
+            if ((<AbstractMesh>this.emitter).position) {
+                var emitterMesh = (<AbstractMesh>this.emitter);
+                serializationObject.emitterId = emitterMesh.id;
+            } else {
+                var emitterPosition = (<Vector3>this.emitter);
+                serializationObject.emitter = emitterPosition.asArray();
+            }
+
+            serializationObject.capacity = this.getCapacity();
+
+            if (this.particleTexture) {
+                serializationObject.textureName = this.particleTexture.name;
+            }
+
+            // Animations
+            Animation.AppendSerializedAnimations(this, serializationObject);
+
+            // Particle system
+            serializationObject.activeParticleCount = this.activeParticleCount;
+            serializationObject.randomTextureSize = this._randomTextureSize;
+            serializationObject.minSize = this.minSize;
+            serializationObject.maxSize = this.maxSize;
+            serializationObject.minEmitPower = this.minEmitPower;
+            serializationObject.maxEmitPower = this.maxEmitPower;
+            serializationObject.minLifeTime = this.minLifeTime;
+            serializationObject.maxLifeTime = this.maxLifeTime;
+            serializationObject.emitRate = this.emitRate;
+            serializationObject.gravity = this.gravity.asArray();
+            serializationObject.color1 = this.color1.asArray();
+            serializationObject.color2 = this.color2.asArray();
+            serializationObject.colorDead = this.colorDead.asArray();
+            serializationObject.updateSpeed = this.updateSpeed;
+            serializationObject.targetStopDuration = this.targetStopDuration;
+            serializationObject.blendMode = this.blendMode;
+
+            // Emitters
+            if (this.particleEmitterType) {
+                
+            }
+
+            return serializationObject;            
         }
     }
 }

+ 75 - 12
src/Particles/babylon.particleSystem.ts

@@ -36,7 +36,7 @@
         animate(): void;
         /**
          * Renders the particle system in its current state.
-         * @returns the current number of particles.
+         * @returns the current number of particles
          */
         render(): number;
         /**
@@ -118,7 +118,7 @@
         public updateSpeed = 0.01;
 
         /**
-         * The amount of time the particle system is running (depends of the overall speed above).
+         * The amount of time the particle system is running (depends of the overall update speed).
          */
         public targetStopDuration = 0;
 
@@ -213,23 +213,77 @@
          */
         public gravity = Vector3.Zero();
 
-        /**
+       /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
+         * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
-        public direction1 = new Vector3(0, 1.0, 0);
+        public get direction1(): Vector3 {
+            if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
+                return (<BoxParticleEmitter>this.particleEmitterType).direction1;
+            }
+
+            return Vector3.Zero();
+        }
+
+        public set direction1(value: Vector3) {
+            if ((<BoxParticleEmitter>this.particleEmitterType).direction1) {
+                (<BoxParticleEmitter>this.particleEmitterType).direction1 = value;
+            }
+        }
+
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
+         * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
-        public direction2 = new Vector3(0, 1.0, 0);
+        public get direction2(): Vector3 {
+            if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
+                return (<BoxParticleEmitter>this.particleEmitterType).direction2;
+            }
+
+            return Vector3.Zero();
+        }
+
+        public set direction2(value: Vector3) {
+            if ((<BoxParticleEmitter>this.particleEmitterType).direction2) {
+                (<BoxParticleEmitter>this.particleEmitterType).direction2 = value;
+            }
+        }
 
         /**
          * Minimum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
+         * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
-        public minEmitBox = new Vector3(-0.5, -0.5, -0.5);
+        public get minEmitBox(): Vector3 {
+            if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
+                return (<BoxParticleEmitter>this.particleEmitterType).minEmitBox;
+            }
+
+            return Vector3.Zero();
+        }
+
+        public set minEmitBox(value: Vector3) {
+            if ((<BoxParticleEmitter>this.particleEmitterType).minEmitBox) {
+                (<BoxParticleEmitter>this.particleEmitterType).minEmitBox = value;
+            }
+        }
+
         /**
          * Maximum box point around our emitter. Our emitter is the center of particles source, but if you want your particles to emit from more than one point, then you can tell it to do so.
+         * This only works when particleEmitterTyps is a BoxParticleEmitter
          */
-        public maxEmitBox = new Vector3(0.5, 0.5, 0.5);
+        public get maxEmitBox(): Vector3 {
+            if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
+                return (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox;
+            }
+
+            return Vector3.Zero();
+        }
+
+        public set maxEmitBox(value: Vector3) {
+            if ((<BoxParticleEmitter>this.particleEmitterType).maxEmitBox) {
+                (<BoxParticleEmitter>this.particleEmitterType).maxEmitBox = value;
+            }
+        }
 
         /**
          * Random color of each particle after it has been emitted, between color1 and color2 vectors.
@@ -341,6 +395,14 @@
         private _isAnimationSheetEnabled: boolean;
 
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        public getClassName(): string {
+            return "ParticleSystem";
+        }        
+
+        /**
          * Instantiates a particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * @param name The name of the particle system
@@ -387,8 +449,8 @@
             this._vertexBuffers[VertexBuffer.ColorKind] = colors;
             this._vertexBuffers["options"] = options;
 
-            // Default behaviors
-            this.particleEmitterType = new BoxParticleEmitter(this);
+            // Default emitter type
+            this.particleEmitterType = new BoxParticleEmitter();
 
             this.updateFunction = (particles: Particle[]): void => {
                 for (var index = 0; index < particles.length; index++) {
@@ -767,14 +829,15 @@
 
         /**
          * Renders the particle system in its current state.
-         * @returns the current number of particles.
+         * @returns the current number of particles
          */
         public render(): number {
             var effect = this._getEffect();
 
             // Check
-            if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady() || !this._particles.length)
+            if (!this.emitter || !effect.isReady() || !this.particleTexture || !this.particleTexture.isReady() || !this._particles.length) {
                 return 0;
+            }
 
             var engine = this._scene.getEngine();
 
@@ -898,7 +961,7 @@
          * @returns the emitter
          */
         public createBoxEmitter(direction1: Vector3, direction2: Vector3, minEmitBox: Vector3, maxEmitBox: Vector3): BoxParticleEmitter {
-            var particleEmitter = new BoxParticleEmitter(this);
+            var particleEmitter = new BoxParticleEmitter();
             this.direction1 = direction1;
             this.direction2 = direction2;
             this.minEmitBox = minEmitBox;

+ 99 - 5
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -1,16 +1,43 @@
 #version 300 es
 
+#define PI 3.14159
+
 uniform float currentCount;
 uniform float timeDelta;
-uniform float generalRandom;
+uniform vec3 generalRandoms;
 uniform mat4 emitterWM;
 uniform vec2 lifeTime;
+uniform vec2 emitPower;
 uniform vec2 sizeRange;
 uniform vec4 color1;
 uniform vec4 color2;
 uniform vec3 gravity;
 uniform sampler2D randomSampler;
 
+#ifdef BOXEMITTER
+uniform vec3 direction1;
+uniform vec3 direction2;
+uniform vec3 minEmitBox;
+uniform vec3 maxEmitBox;
+#endif
+
+#ifdef SPHEREEMITTER
+uniform float radius;
+  #ifdef DIRECTEDSPHEREEMITTER
+  uniform vec3 direction1;
+  uniform vec3 direction2;
+  #else
+  uniform float directionRandomizer;
+  #endif
+#endif
+
+#ifdef CONEEMITTER
+uniform float radius;
+uniform float angle;
+uniform float height;
+uniform float directionRandomizer;
+#endif
+
 // Particles state
 in vec3 position;
 in float age;
@@ -33,13 +60,18 @@ vec3 getRandomVec3(float offset) {
   return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 0)).rgb;
 }
 
+vec4 getRandomVec4(float offset) {
+  return texture(randomSampler, vec2(float(gl_VertexID) * offset / currentCount, 0));
+}
+
+
 void main() {
   if (age >= life) {
-    // Create the particle at origin
-    outPosition = (emitterWM * vec4(0., 0., 0., 1.)).xyz;
+    vec3 position;
+    vec3 direction;
 
     // Let's get some random values
-    vec3 randoms = getRandomVec3(generalRandom);
+    vec4 randoms = getRandomVec4(generalRandoms.x);
 
     // Age and life
     outAge = 0.0;
@@ -54,8 +86,70 @@ void main() {
     // Color
     outColor = color1 + (color2 - color1) * randoms.b;
 
+    // Position / Direction (based on emitter type)
+#ifdef BOXEMITTER
+    vec3 randoms2 = getRandomVec3(generalRandoms.y);
+    vec3 randoms3 = getRandomVec3(generalRandoms.z);
+
+    position = minEmitBox + (maxEmitBox - minEmitBox) * randoms2;
+
+    direction = direction1 + (direction2 - direction1) * randoms3;
+#elif defined(SPHEREEMITTER)
+    vec3 randoms2 = getRandomVec3(generalRandoms.y);
+    vec3 randoms3 = getRandomVec3(generalRandoms.z);
+
+    // Position on the sphere surface
+    float phi = 2.0 * PI * randoms2.x;
+    float theta = PI * randoms2.y;
+    float randX = cos(phi) * sin(theta);
+    float randY = cos(theta);
+    float randZ = sin(phi) * sin(theta);
+
+    position = radius * vec3(randX, randY, randZ);
+
+    #ifdef DIRECTEDSPHEREEMITTER
+      direction = direction1 + (direction2 - direction1) * randoms3;
+    #else
+        // Direction
+        direction = position + directionRandomizer * randoms3;
+    #endif
+#elif defined(CONEEMITTER)
+    vec3 randoms2 = getRandomVec3(generalRandoms.y);
+
+    float s = 2.0 * PI * randoms2.x;
+    float h = randoms2.y;
+    
+    // Better distribution in a cone at normal angles.
+    h = 1. - h * h;
+    float lRadius = radius * randoms2.z;
+    lRadius = lRadius * h / height;
+
+    float randX = lRadius * sin(s);
+    float randZ = lRadius * cos(s);
+    float randY = h  * height;
+
+    position = vec3(randX, randY, randZ); 
+
     // Direction
-    outDirection = 2.0 * (getRandomVec3(seed) - vec3(0.5, 0.5, 0.5));
+    if (angle == 0.) {
+      direction = vec3(0., 1.0, 0.);
+    } else {
+      vec3 randoms3 = getRandomVec3(generalRandoms.z);
+      direction = position + directionRandomizer * randoms3;
+    }
+#else    
+    // Create the particle at origin
+    position = vec3(0., 0., 0.);
+
+    // Spread in all directions
+    direction = 2.0 * (getRandomVec3(seed) - vec3(0.5, 0.5, 0.5));
+#endif
+
+    float power = emitPower.x + (emitPower.y - emitPower.x) * randoms.a;
+
+    outPosition = (emitterWM * vec4(position, 1.)).xyz;
+    outDirection = (emitterWM * vec4(normalize(direction) * power, 0.)).xyz;
+
   } else {   
     outPosition = position + (direction + gravity) * timeDelta;
     outAge = age + timeDelta;