Просмотр исходного кода

New tool for debugLayer: You can now dump renderTargets to see their content
Complete shadows code rework: New bias property for ShadowGenerator, new orthogonal shadows for directional shadows, automatic projection size for directional lights

David Catuhe 11 лет назад
Родитель
Сommit
a31312fef3
33 измененных файлов с 1316 добавлено и 617 удалено
  1. 41 20
      Babylon/Audio/babylon.sound.js
  2. 42 20
      Babylon/Audio/babylon.sound.ts
  3. 14 0
      Babylon/Audio/babylon.soundtrack.js
  4. 16 0
      Babylon/Audio/babylon.soundtrack.ts
  5. 40 5
      Babylon/Debug/babylon.debugLayer.js
  6. 1 1
      Babylon/Debug/babylon.debugLayer.js.map
  7. 41 6
      Babylon/Debug/babylon.debugLayer.ts
  8. 16 5
      Babylon/Lights/Shadows/babylon.shadowGenerator.js
  9. 22 6
      Babylon/Lights/Shadows/babylon.shadowGenerator.ts
  10. 31 0
      Babylon/Lights/babylon.directionalLight.js
  11. 43 0
      Babylon/Lights/babylon.directionalLight.ts
  12. 7 1
      Babylon/Lights/babylon.light.ts
  13. 10 0
      Babylon/Lights/babylon.spotLight.js
  14. 13 0
      Babylon/Lights/babylon.spotLight.ts
  15. 45 28
      Babylon/Loading/Plugins/babylon.babylonFileLoader.js
  16. 46 30
      Babylon/Loading/Plugins/babylon.babylonFileLoader.ts
  17. 5 1
      Babylon/Materials/Textures/babylon.renderTargetTexture.js
  18. 6 1
      Babylon/Materials/Textures/babylon.renderTargetTexture.ts
  19. 2 2
      Babylon/Materials/babylon.standardMaterial.js
  20. 2 2
      Babylon/Materials/babylon.standardMaterial.ts
  21. 6 1
      Babylon/Math/babylon.math.js
  22. 10 2
      Babylon/Math/babylon.math.ts
  23. 24 24
      Babylon/Shaders/default.fragment.fx
  24. 2 2
      Babylon/Shaders/shadowMap.fragment.fx
  25. 2 3
      Babylon/Shaders/shadowMap.vertex.fx
  26. 64 62
      Babylon/Tools/babylon.filesInput.js
  27. 77 71
      Babylon/Tools/babylon.filesInput.ts
  28. 55 48
      Babylon/Tools/babylon.tools.js
  29. 67 60
      Babylon/Tools/babylon.tools.ts
  30. 69 7
      Babylon/babylon.scene.js
  31. 77 8
      Babylon/babylon.scene.ts
  32. 401 182
      babylon.2.1-alpha.debug.js
  33. 19 19
      babylon.2.1-alpha.js

+ 41 - 20
Babylon/Audio/babylon.sound.js

@@ -27,7 +27,8 @@ var BABYLON;
             this._volume = 1;
             this._isLoaded = false;
             this._isReadyToPlay = false;
-            this._isPlaying = false;
+            this.isPlaying = false;
+            this.isPaused = false;
             this._isDirectional = false;
             // Used if you'd like to create a directional sound.
             // If not set, the sound will be omnidirectional
@@ -100,7 +101,7 @@ var BABYLON;
         }
         Sound.prototype.dispose = function () {
             if (BABYLON.Engine.audioEngine.canUseWebAudio && this._isReadyToPlay) {
-                if (this._isPlaying) {
+                if (this.isPlaying) {
                     this.stop();
                 }
                 this._isReadyToPlay = false;
@@ -185,6 +186,17 @@ var BABYLON;
                 this._inputAudioNode = this._soundPanner;
             }
         };
+        Sound.prototype.switchPanningModelToHRTF = function () {
+            this._switchPanningModel("HRTF");
+        };
+        Sound.prototype.switchPanningModelToEqualPower = function () {
+            this._switchPanningModel("equalpower");
+        };
+        Sound.prototype._switchPanningModel = function (newModel) {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio && this.spatialSound) {
+                this._soundPanner.panningModel = newModel;
+            }
+        };
         Sound.prototype.connectToSoundTrackAudioNode = function (soundTrackAudioNode) {
             if (BABYLON.Engine.audioEngine.canUseWebAudio) {
                 this._ouputAudioNode.disconnect();
@@ -206,20 +218,20 @@ var BABYLON;
             this._coneOuterAngle = coneOuterAngle;
             this._coneOuterGain = coneOuterGain;
             this._isDirectional = true;
-            if (this._isPlaying && this.loop) {
+            if (this.isPlaying && this.loop) {
                 this.stop();
                 this.play();
             }
         };
         Sound.prototype.setPosition = function (newPosition) {
             this._position = newPosition;
-            if (this._isPlaying && this.spatialSound) {
+            if (this.isPlaying && this.spatialSound) {
                 this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
             }
         };
         Sound.prototype.setLocalDirectionToMesh = function (newLocalDirection) {
             this._localDirection = newLocalDirection;
-            if (this._connectedMesh && this._isPlaying) {
+            if (this._connectedMesh && this.isPlaying) {
                 this._updateDirection();
             }
         };
@@ -243,9 +255,10 @@ var BABYLON;
         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
         */
         Sound.prototype.play = function (time) {
-            if (this._isReadyToPlay) {
+            var _this = this;
+            if (this._isReadyToPlay && this._scene.audioEnabled) {
                 try {
-                    var startTime = time ? BABYLON.Engine.audioEngine.audioContext.currentTime + time : 0;
+                    var startTime = time ? BABYLON.Engine.audioEngine.audioContext.currentTime + time : BABYLON.Engine.audioEngine.audioContext.currentTime;
                     if (!this._soundSource) {
                         if (this.spatialSound) {
                             this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
@@ -268,32 +281,40 @@ var BABYLON;
                     this._soundSource.loop = this.loop;
                     this._soundSource.playbackRate.value = this._playbackRate;
                     this._startTime = startTime;
-                    if (this.onended) {
-                        this._soundSource.onended = this.onended;
-                    }
-                    this._soundSource.start(startTime, this._startOffset % this._soundSource.buffer.duration);
-                    this._isPlaying = true;
+                    this._soundSource.onended = function () {
+                        _this._onended();
+                    };
+                    this._soundSource.start(this._startTime, this.isPaused ? this._startOffset % this._soundSource.buffer.duration : 0);
+                    this.isPlaying = true;
+                    this.isPaused = false;
                 }
                 catch (ex) {
                     BABYLON.Tools.Error("Error while trying to play audio: " + this.name + ", " + ex.message);
                 }
             }
         };
+        Sound.prototype._onended = function () {
+            this.isPlaying = false;
+            if (this.onended) {
+                this.onended();
+            }
+        };
         /**
         * Stop the sound
         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
         */
         Sound.prototype.stop = function (time) {
-            if (this._isPlaying) {
-                var stopTime = time ? BABYLON.Engine.audioEngine.audioContext.currentTime + time : 0;
+            if (this.isPlaying) {
+                var stopTime = time ? BABYLON.Engine.audioEngine.audioContext.currentTime + time : BABYLON.Engine.audioEngine.audioContext.currentTime;
                 this._soundSource.stop(stopTime);
-                this._isPlaying = false;
+                this.isPlaying = false;
             }
         };
         Sound.prototype.pause = function () {
-            if (this._isPlaying) {
-                this._soundSource.stop(0);
+            if (this.isPlaying) {
+                this.stop(0);
                 this._startOffset += BABYLON.Engine.audioEngine.audioContext.currentTime - this._startTime;
+                this.isPaused = true;
             }
         };
         Sound.prototype.setVolume = function (newVolume, time) {
@@ -310,7 +331,7 @@ var BABYLON;
         };
         Sound.prototype.setPlaybackRate = function (newPlaybackRate) {
             this._playbackRate = newPlaybackRate;
-            if (this._isPlaying) {
+            if (this.isPlaying) {
                 this._soundSource.playbackRate.value = this._playbackRate;
             }
         };
@@ -323,7 +344,7 @@ var BABYLON;
             if (!this.spatialSound) {
                 this._createSpatialParameters();
                 this.spatialSound = true;
-                if (this._isPlaying && this.loop) {
+                if (this.isPlaying && this.loop) {
                     this.stop();
                     this.play();
                 }
@@ -334,7 +355,7 @@ var BABYLON;
         };
         Sound.prototype._onRegisterAfterWorldMatrixUpdate = function (connectedMesh) {
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
-            if (this._isDirectional && this._isPlaying) {
+            if (this._isDirectional && this.isPlaying) {
                 this._updateDirection();
             }
         };

+ 42 - 20
Babylon/Audio/babylon.sound.ts

@@ -20,7 +20,8 @@
         private _volume: number = 1;
         private _isLoaded: boolean = false;
         private _isReadyToPlay: boolean = false;
-        private _isPlaying: boolean = false;
+        public isPlaying: boolean = false;
+        public isPaused: boolean = false;
         private _isDirectional: boolean = false;
         private _readyToPlayCallback: () => any;
         private _audioBuffer: AudioBuffer;
@@ -113,7 +114,7 @@
 
         public dispose() {
             if (Engine.audioEngine.canUseWebAudio && this._isReadyToPlay) {
-                if (this._isPlaying) {
+                if (this.isPlaying) {
                     this.stop();
                 }
                 this._isReadyToPlay = false;
@@ -198,6 +199,20 @@
             }
         }
 
+        public switchPanningModelToHRTF() {
+            this._switchPanningModel("HRTF");    
+        }
+
+        public switchPanningModelToEqualPower() {
+            this._switchPanningModel("equalpower");
+        }
+
+        private _switchPanningModel(newModel: string) {
+            if (Engine.audioEngine.canUseWebAudio && this.spatialSound) {
+                this._soundPanner.panningModel = newModel;
+            }
+        }
+
         public connectToSoundTrackAudioNode(soundTrackAudioNode: AudioNode) {
             if (Engine.audioEngine.canUseWebAudio) {
                 this._ouputAudioNode.disconnect();
@@ -221,7 +236,7 @@
             this._coneOuterGain = coneOuterGain;
             this._isDirectional = true;
 
-            if (this._isPlaying && this.loop) {
+            if (this.isPlaying && this.loop) {
                 this.stop();
                 this.play();
             }
@@ -230,7 +245,7 @@
         public setPosition(newPosition: Vector3) {
             this._position = newPosition;
 
-            if (this._isPlaying && this.spatialSound) {
+            if (this.isPlaying && this.spatialSound) {
                 this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
             }
         }
@@ -238,7 +253,7 @@
         public setLocalDirectionToMesh(newLocalDirection: Vector3) {
             this._localDirection = newLocalDirection;
 
-            if (this._connectedMesh && this._isPlaying) {
+            if (this._connectedMesh && this.isPlaying) {
                 this._updateDirection();
             }
         }
@@ -266,9 +281,9 @@
         * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
         */
         public play(time?: number) {
-            if (this._isReadyToPlay) {
+            if (this._isReadyToPlay && this._scene.audioEnabled) {
                 try {
-                    var startTime = time ? Engine.audioEngine.audioContext.currentTime + time : 0;
+                    var startTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                     if (!this._soundSource) {
                         if (this.spatialSound) {
                             this._soundPanner.setPosition(this._position.x, this._position.y, this._position.z);
@@ -291,11 +306,10 @@
                     this._soundSource.loop = this.loop;
                     this._soundSource.playbackRate.value = this._playbackRate;
                     this._startTime = startTime;
-                    if (this.onended) {
-                        this._soundSource.onended = this.onended;
-                    }
-                    this._soundSource.start(startTime, this._startOffset % this._soundSource.buffer.duration);
-                    this._isPlaying = true;
+                    this._soundSource.onended = () => { this._onended(); };
+                    this._soundSource.start(this._startTime, this.isPaused ? this._startOffset % this._soundSource.buffer.duration : 0);
+                    this.isPlaying = true;
+                    this.isPaused = false;
                 }
                 catch (ex) {
                     Tools.Error("Error while trying to play audio: " + this.name + ", " + ex.message);
@@ -303,22 +317,30 @@
             }
         }
 
+        private _onended() {
+            this.isPlaying = false;
+            if (this.onended) {
+                this.onended();
+            }
+        }
+
         /**
         * Stop the sound
         * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
         */
         public stop(time?: number) {
-            if (this._isPlaying) {
-                var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : 0;
+            if (this.isPlaying) {
+                var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                 this._soundSource.stop(stopTime);
-                this._isPlaying = false;
+                this.isPlaying = false;
             }
         }
 
         public pause() {
-            if (this._isPlaying) {
-                this._soundSource.stop(0);
+            if (this.isPlaying) {
+                this.stop(0);
                 this._startOffset += Engine.audioEngine.audioContext.currentTime - this._startTime;
+                this.isPaused = true;
             }
         }
 
@@ -337,7 +359,7 @@
 
         public setPlaybackRate(newPlaybackRate: number) {
             this._playbackRate = newPlaybackRate;
-            if (this._isPlaying) {
+            if (this.isPlaying) {
                 this._soundSource.playbackRate.value = this._playbackRate;
             }
         }
@@ -351,7 +373,7 @@
             if (!this.spatialSound) {
                 this._createSpatialParameters();
                 this.spatialSound = true;
-                if (this._isPlaying && this.loop) {
+                if (this.isPlaying && this.loop) {
                     this.stop();
                     this.play();
                 }
@@ -363,7 +385,7 @@
 
         private _onRegisterAfterWorldMatrixUpdate(connectedMesh: AbstractMesh) {
             this.setPosition(connectedMesh.getBoundingInfo().boundingSphere.centerWorld);
-            if (this._isDirectional && this._isPlaying) {
+            if (this._isDirectional && this.isPlaying) {
                 this._updateDirection();
             }
         }

+ 14 - 0
Babylon/Audio/babylon.soundtrack.js

@@ -62,6 +62,20 @@ var BABYLON;
                 this._trackGain.gain.value = newVolume;
             }
         };
+        SoundTrack.prototype.switchPanningModelToHRTF = function () {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio) {
+                for (var i = 0; i < this.soundCollection.length; i++) {
+                    this.soundCollection[i].switchPanningModelToHRTF();
+                }
+            }
+        };
+        SoundTrack.prototype.switchPanningModelToEqualPower = function () {
+            if (BABYLON.Engine.audioEngine.canUseWebAudio) {
+                for (var i = 0; i < this.soundCollection.length; i++) {
+                    this.soundCollection[i].switchPanningModelToEqualPower();
+                }
+            }
+        };
         SoundTrack.prototype.connectToAnalyser = function (analyser) {
             if (this._connectedAnalyser) {
                 this._connectedAnalyser.stopDebugCanvas();

+ 16 - 0
Babylon/Audio/babylon.soundtrack.ts

@@ -71,6 +71,22 @@
             }
         }
 
+        public switchPanningModelToHRTF() {
+            if (Engine.audioEngine.canUseWebAudio) {
+                for (var i = 0; i < this.soundCollection.length; i++) {
+                    this.soundCollection[i].switchPanningModelToHRTF();
+                }
+            }
+        }
+
+        public switchPanningModelToEqualPower() {
+            if (Engine.audioEngine.canUseWebAudio) {
+                for (var i = 0; i < this.soundCollection.length; i++) {
+                    this.soundCollection[i].switchPanningModelToEqualPower();
+                }
+            }
+        }
+
         public connectToAnalyser(analyser: Analyser) {
             if (this._connectedAnalyser) {
                 this._connectedAnalyser.stopDebugCanvas();

+ 40 - 5
Babylon/Debug/babylon.debugLayer.js

@@ -336,17 +336,29 @@ var BABYLON;
         DebugLayer.prototype._generateCheckBox = function (root, title, initialState, task, tag) {
             if (tag === void 0) { tag = null; }
             var label = document.createElement("label");
-            var boundingBoxesCheckbox = document.createElement("input");
-            boundingBoxesCheckbox.type = "checkbox";
-            boundingBoxesCheckbox.checked = initialState;
-            boundingBoxesCheckbox.addEventListener("change", function (evt) {
+            var checkBox = document.createElement("input");
+            checkBox.type = "checkbox";
+            checkBox.checked = initialState;
+            checkBox.addEventListener("change", function (evt) {
                 task(evt.target, tag);
             });
-            label.appendChild(boundingBoxesCheckbox);
+            label.appendChild(checkBox);
             label.appendChild(document.createTextNode(title));
             root.appendChild(label);
             root.appendChild(document.createElement("br"));
         };
+        DebugLayer.prototype._generateButton = function (root, title, task, tag) {
+            if (tag === void 0) { tag = null; }
+            var button = document.createElement("button");
+            button.innerHTML = title;
+            button.style.height = "20px";
+            button.style.color = "#222222";
+            button.addEventListener("click", function (evt) {
+                task(evt.target, tag);
+            });
+            root.appendChild(button);
+            root.appendChild(document.createElement("br"));
+        };
         DebugLayer.prototype._generateRadio = function (root, title, name, initialState, task, tag) {
             if (tag === void 0) { tag = null; }
             var label = document.createElement("label");
@@ -561,6 +573,29 @@ var BABYLON;
                 this._generateCheckBox(this._optionsSubsetDiv, "Textures", this._scene.texturesEnabled, function (element) {
                     _this._scene.texturesEnabled = element.checked;
                 });
+                if (BABYLON.Engine.audioEngine.canUseWebAudio) {
+                    this._optionsSubsetDiv.appendChild(document.createElement("br"));
+                    this._generateTexBox(this._optionsSubsetDiv, "<b>Audio:</b>", this.accentColor);
+                    this._generateRadio(this._optionsSubsetDiv, "Headphones", "panningModel", true, function (element) {
+                        if (element.checked) {
+                            _this._scene.switchAudioModeForHeadphones();
+                        }
+                    });
+                    this._generateRadio(this._optionsSubsetDiv, "Normal Speakers", "panningModel", false, function (element) {
+                        if (element.checked) {
+                            _this._scene.switchAudioModeForNormalSpeakers();
+                        }
+                    });
+                    this._generateCheckBox(this._optionsSubsetDiv, "Disable audio", !this._scene.audioEnabled, function (element) {
+                        _this._scene.audioEnabled = !element.checked;
+                    });
+                }
+                this._optionsSubsetDiv.appendChild(document.createElement("br"));
+                this._generateTexBox(this._optionsSubsetDiv, "<b>Tools:</b>", this.accentColor);
+                this._generateButton(this._optionsSubsetDiv, "Dump rendertargets", function (element) {
+                    _this._scene.dumpNextRenderTargets = true;
+                });
+                this._optionsSubsetDiv.appendChild(document.createElement("br"));
                 this._globalDiv.appendChild(this._statsDiv);
                 this._globalDiv.appendChild(this._logDiv);
                 this._globalDiv.appendChild(this._optionsDiv);

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
Babylon/Debug/babylon.debugLayer.js.map


+ 41 - 6
Babylon/Debug/babylon.debugLayer.ts

@@ -460,20 +460,34 @@
         private _generateCheckBox(root: HTMLDivElement, title: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
             var label = document.createElement("label");
 
-            var boundingBoxesCheckbox = document.createElement("input");
-            boundingBoxesCheckbox.type = "checkbox";
-            boundingBoxesCheckbox.checked = initialState;
+            var checkBox = document.createElement("input");
+            checkBox.type = "checkbox";
+            checkBox.checked = initialState;
 
-            boundingBoxesCheckbox.addEventListener("change", (evt: Event) => {
+            checkBox.addEventListener("change", (evt: Event) => {
                 task(evt.target, tag);
             });
 
-            label.appendChild(boundingBoxesCheckbox);
+            label.appendChild(checkBox);
             label.appendChild(document.createTextNode(title));
             root.appendChild(label);
             root.appendChild(document.createElement("br"));
         }
 
+        private _generateButton(root: HTMLDivElement, title: string, task: (element, tag) => void, tag: any = null): void {
+            var button = document.createElement("button");
+            button.innerHTML = title;
+            button.style.height = "20px";
+            button.style.color = "#222222";
+
+            button.addEventListener("click",(evt: Event) => {
+                task(evt.target, tag);
+            });
+
+            root.appendChild(button);
+            root.appendChild(document.createElement("br"));
+        }
+
         private _generateRadio(root: HTMLDivElement, title: string, name: string, initialState: boolean, task: (element, tag) => void, tag: any = null): void {
             var label = document.createElement("label");
 
@@ -650,7 +664,28 @@
                 this._generateCheckBox(this._optionsSubsetDiv, "Skeletons", this._scene.skeletonsEnabled, (element) => { this._scene.skeletonsEnabled = element.checked });
                 this._generateCheckBox(this._optionsSubsetDiv, "Sprites", this._scene.spritesEnabled, (element) => { this._scene.spritesEnabled = element.checked });
                 this._generateCheckBox(this._optionsSubsetDiv, "Textures", this._scene.texturesEnabled, (element) => { this._scene.texturesEnabled = element.checked });
-
+                if (Engine.audioEngine.canUseWebAudio) {
+                    this._optionsSubsetDiv.appendChild(document.createElement("br"));
+                    this._generateTexBox(this._optionsSubsetDiv, "<b>Audio:</b>", this.accentColor);
+                    this._generateRadio(this._optionsSubsetDiv, "Headphones", "panningModel", true, (element) => {
+                        if (element.checked) {
+                            this._scene.switchAudioModeForHeadphones();
+                        }
+                    });
+                    this._generateRadio(this._optionsSubsetDiv, "Normal Speakers", "panningModel", false, (element) => {
+                        if (element.checked) {
+                            this._scene.switchAudioModeForNormalSpeakers();
+                        }
+                    });
+                    this._generateCheckBox(this._optionsSubsetDiv, "Disable audio", !this._scene.audioEnabled, (element) => {
+                        this._scene.audioEnabled = !element.checked;
+                    });
+                }
+                this._optionsSubsetDiv.appendChild(document.createElement("br"));
+                this._generateTexBox(this._optionsSubsetDiv, "<b>Tools:</b>", this.accentColor);
+                this._generateButton(this._optionsSubsetDiv, "Dump rendertargets",(element) => { this._scene.dumpNextRenderTargets = true; });
+                this._optionsSubsetDiv.appendChild(document.createElement("br"));
+  
                 this._globalDiv.appendChild(this._statsDiv);
                 this._globalDiv.appendChild(this._logDiv);
                 this._globalDiv.appendChild(this._optionsDiv);

+ 16 - 5
Babylon/Lights/Shadows/babylon.shadowGenerator.js

@@ -6,6 +6,7 @@ var BABYLON;
             // Members
             this.filter = ShadowGenerator.FILTER_NONE;
             this._darkness = 0;
+            this._bias = 0.0001;
             this._transparencyShadow = false;
             this._viewMatrix = BABYLON.Matrix.Zero();
             this._projectionMatrix = BABYLON.Matrix.Zero();
@@ -94,7 +95,7 @@ var BABYLON;
         });
         Object.defineProperty(ShadowGenerator.prototype, "useVarianceShadowMap", {
             get: function () {
-                return this.filter === ShadowGenerator.FILTER_VARIANCESHADOWMAP;
+                return this.filter === ShadowGenerator.FILTER_VARIANCESHADOWMAP && this._light.supportsVSM();
             },
             set: function (value) {
                 this.filter = (value ? ShadowGenerator.FILTER_VARIANCESHADOWMAP : ShadowGenerator.FILTER_NONE);
@@ -104,7 +105,7 @@ var BABYLON;
         });
         Object.defineProperty(ShadowGenerator.prototype, "usePoissonSampling", {
             get: function () {
-                return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
+                return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING || (this.filter === ShadowGenerator.FILTER_VARIANCESHADOWMAP && !this._light.supportsVSM());
             },
             set: function (value) {
                 this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
@@ -163,17 +164,21 @@ var BABYLON;
         };
         // Methods
         ShadowGenerator.prototype.getTransformMatrix = function () {
+            var scene = this._scene;
+            if (this._currentRenderID === scene.getRenderId()) {
+                return this._transformMatrix;
+            }
+            this._currentRenderID = scene.getRenderId();
             var lightPosition = this._light.position;
             var lightDirection = this._light.direction;
             if (this._light.computeTransformedPosition()) {
                 lightPosition = this._light.transformedPosition;
             }
-            if (!this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !lightDirection.equals(this._cachedDirection)) {
+            if (this._light.needRefreshPerFrame() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !lightDirection.equals(this._cachedDirection)) {
                 this._cachedPosition = lightPosition.clone();
                 this._cachedDirection = lightDirection.clone();
-                var activeCamera = this._scene.activeCamera;
                 BABYLON.Matrix.LookAtLHToRef(lightPosition, this._light.position.add(lightDirection), BABYLON.Vector3.Up(), this._viewMatrix);
-                BABYLON.Matrix.PerspectiveFovLHToRef(Math.PI / 2.0, 1.0, activeCamera.minZ, activeCamera.maxZ, this._projectionMatrix);
+                this._light.setShadowProjectionMatrix(this._projectionMatrix, this._viewMatrix, this.getShadowMap().renderList);
                 this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
             }
             return this._transformMatrix;
@@ -189,6 +194,12 @@ var BABYLON;
             else
                 this._darkness = darkness;
         };
+        ShadowGenerator.prototype.getBias = function () {
+            return this._bias;
+        };
+        ShadowGenerator.prototype.setBias = function (bias) {
+            this._bias = bias;
+        };
         ShadowGenerator.prototype.setTransparencyShadow = function (hasShadow) {
             this._transparencyShadow = hasShadow;
         };

+ 22 - 6
Babylon/Lights/Shadows/babylon.shadowGenerator.ts

@@ -21,14 +21,14 @@
         public filter = ShadowGenerator.FILTER_NONE;
 
         public get useVarianceShadowMap(): boolean {
-            return this.filter === ShadowGenerator.FILTER_VARIANCESHADOWMAP;
+            return this.filter === ShadowGenerator.FILTER_VARIANCESHADOWMAP && this._light.supportsVSM();
         }
         public set useVarianceShadowMap(value: boolean) {
             this.filter = (value ? ShadowGenerator.FILTER_VARIANCESHADOWMAP : ShadowGenerator.FILTER_NONE);
         }
 
         public get usePoissonSampling(): boolean {
-            return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING;
+            return this.filter === ShadowGenerator.FILTER_POISSONSAMPLING || (this.filter === ShadowGenerator.FILTER_VARIANCESHADOWMAP && !this._light.supportsVSM());
         }
         public set usePoissonSampling(value: boolean) {
             this.filter = (value ? ShadowGenerator.FILTER_POISSONSAMPLING : ShadowGenerator.FILTER_NONE);
@@ -38,6 +38,7 @@
         private _scene: Scene;
         private _shadowMap: RenderTargetTexture;
         private _darkness = 0;
+        private _bias = 0.0001;
         private _transparencyShadow = false;
         private _effect: Effect;
 
@@ -48,6 +49,7 @@
         private _cachedPosition: Vector3;
         private _cachedDirection: Vector3;
         private _cachedDefines: string;
+        private _currentRenderID: number;
 
         constructor(mapSize: number, light: IShadowLight) {
             this._light = light;
@@ -192,6 +194,13 @@
 
         // Methods
         public getTransformMatrix(): Matrix {
+            var scene = this._scene;
+            if (this._currentRenderID === scene.getRenderId()) {
+                return this._transformMatrix;
+            }
+
+            this._currentRenderID = scene.getRenderId();
+
             var lightPosition = this._light.position;
             var lightDirection = this._light.direction;
 
@@ -199,15 +208,14 @@
                 lightPosition = this._light.transformedPosition;
             }
 
-            if (!this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !lightDirection.equals(this._cachedDirection)) {
+            if (this._light.needRefreshPerFrame() || !this._cachedPosition || !this._cachedDirection || !lightPosition.equals(this._cachedPosition) || !lightDirection.equals(this._cachedDirection)) {
 
                 this._cachedPosition = lightPosition.clone();
                 this._cachedDirection = lightDirection.clone();
 
-                var activeCamera = this._scene.activeCamera;
-
                 Matrix.LookAtLHToRef(lightPosition, this._light.position.add(lightDirection), Vector3.Up(), this._viewMatrix);
-                Matrix.PerspectiveFovLHToRef(Math.PI / 2.0, 1.0, activeCamera.minZ, activeCamera.maxZ, this._projectionMatrix);
+
+                this._light.setShadowProjectionMatrix(this._projectionMatrix, this._viewMatrix, this.getShadowMap().renderList);
 
                 this._viewMatrix.multiplyToRef(this._projectionMatrix, this._transformMatrix);
             }
@@ -228,6 +236,14 @@
                 this._darkness = darkness;
         }
 
+        public getBias(): number {
+            return this._bias;
+        }
+
+        public setBias(bias: number): void {
+            this._bias = bias;
+        }
+
         public setTransparencyShadow(hasShadow: boolean): void {
             this._transparencyShadow = hasShadow;
         }

+ 31 - 0
Babylon/Lights/babylon.directionalLight.js

@@ -20,6 +20,37 @@ var BABYLON;
             this.direction = BABYLON.Vector3.Normalize(target.subtract(this.position));
             return this.direction;
         };
+        DirectionalLight.prototype.setShadowProjectionMatrix = function (matrix, viewMatrix, renderList) {
+            var orthoLeft = Number.MAX_VALUE;
+            var orthoRight = Number.MIN_VALUE;
+            var orthoTop = Number.MIN_VALUE;
+            var orthoBottom = Number.MAX_VALUE;
+            var tempVector3 = BABYLON.Vector3.Zero();
+            var activeCamera = this.getScene().activeCamera;
+            for (var meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
+                var boundingBox = renderList[meshIndex].getBoundingInfo().boundingBox;
+                for (var index = 0; index < boundingBox.vectorsWorld.length; index++) {
+                    BABYLON.Vector3.TransformCoordinatesToRef(boundingBox.vectorsWorld[index], viewMatrix, tempVector3);
+                    if (tempVector3.x < orthoLeft)
+                        orthoLeft = tempVector3.x;
+                    if (tempVector3.y < orthoBottom)
+                        orthoBottom = tempVector3.y;
+                    if (tempVector3.x > orthoRight)
+                        orthoRight = tempVector3.x;
+                    if (tempVector3.y > orthoTop)
+                        orthoTop = tempVector3.y;
+                }
+            }
+            var orthoWidth = Math.max(Math.abs(orthoRight), Math.abs(orthoLeft)) * 1.1;
+            var orthoHeight = Math.max(Math.abs(orthoTop), Math.abs(orthoBottom)) * 1.1;
+            BABYLON.Matrix.OrthoOffCenterLHToRef(-orthoWidth, orthoWidth, -orthoHeight, orthoHeight, activeCamera.minZ, activeCamera.maxZ, matrix);
+        };
+        DirectionalLight.prototype.supportsVSM = function () {
+            return false;
+        };
+        DirectionalLight.prototype.needRefreshPerFrame = function () {
+            return true;
+        };
         DirectionalLight.prototype.computeTransformedPosition = function () {
             if (this.parent && this.parent.getWorldMatrix) {
                 if (!this.transformedPosition) {

+ 43 - 0
Babylon/Lights/babylon.directionalLight.ts

@@ -21,6 +21,49 @@
             return this.direction;
         }
 
+        public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void {
+            var orthoLeft = Number.MAX_VALUE;
+            var orthoRight = Number.MIN_VALUE;
+            var orthoTop = Number.MIN_VALUE;
+            var orthoBottom = Number.MAX_VALUE;
+
+            var tempVector3 = Vector3.Zero();
+
+            var activeCamera = this.getScene().activeCamera;
+
+            // Check extends
+            for (var meshIndex = 0; meshIndex < renderList.length; meshIndex++) {
+                var boundingBox = renderList[meshIndex].getBoundingInfo().boundingBox;
+
+                for (var index = 0; index < boundingBox.vectorsWorld.length; index++) {
+                    Vector3.TransformCoordinatesToRef(boundingBox.vectorsWorld[index], viewMatrix, tempVector3);
+
+                    if (tempVector3.x < orthoLeft)
+                        orthoLeft = tempVector3.x;
+                    if (tempVector3.y < orthoBottom)
+                        orthoBottom = tempVector3.y;
+
+                    if (tempVector3.x > orthoRight)
+                        orthoRight = tempVector3.x;
+                    if (tempVector3.y > orthoTop)
+                        orthoTop = tempVector3.y;
+                }
+            }
+
+            var orthoWidth = Math.max(Math.abs(orthoRight), Math.abs(orthoLeft)) * 1.1;
+            var orthoHeight = Math.max(Math.abs(orthoTop), Math.abs(orthoBottom)) * 1.1;
+
+            Matrix.OrthoOffCenterLHToRef(-orthoWidth, orthoWidth, -orthoHeight, orthoHeight, activeCamera.minZ, activeCamera.maxZ, matrix);
+        }
+
+        public supportsVSM(): boolean {
+            return false;
+        }
+
+        public needRefreshPerFrame(): boolean {
+            return true;
+        }
+
         public computeTransformedPosition(): boolean {
             if (this.parent && this.parent.getWorldMatrix) {
                 if (!this.transformedPosition) {

+ 7 - 1
Babylon/Lights/babylon.light.ts

@@ -9,6 +9,12 @@
         computeTransformedPosition(): boolean;
         getScene(): Scene;
 
+        setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void;
+
+        supportsVSM(): boolean;
+
+        needRefreshPerFrame(): boolean;
+
         _shadowGenerator: ShadowGenerator;
     }
 
@@ -38,7 +44,7 @@
         public getAbsolutePosition(): Vector3 {
             return Vector3.Zero();
         }
-
+       
         public transferToEffect(effect: Effect, uniformName0?: string, uniformName1?: string): void {
         }
 

+ 10 - 0
Babylon/Lights/babylon.spotLight.js

@@ -18,6 +18,16 @@ var BABYLON;
         SpotLight.prototype.getAbsolutePosition = function () {
             return this.transformedPosition ? this.transformedPosition : this.position;
         };
+        SpotLight.prototype.setShadowProjectionMatrix = function (matrix, viewMatrix, renderList) {
+            var activeCamera = this.getScene().activeCamera;
+            BABYLON.Matrix.PerspectiveFovLHToRef(this.angle, 1.0, activeCamera.minZ, activeCamera.maxZ, matrix);
+        };
+        SpotLight.prototype.supportsVSM = function () {
+            return true;
+        };
+        SpotLight.prototype.needRefreshPerFrame = function () {
+            return false;
+        };
         SpotLight.prototype.setDirectionToTarget = function (target) {
             this.direction = BABYLON.Vector3.Normalize(target.subtract(this.position));
             return this.direction;

+ 13 - 0
Babylon/Lights/babylon.spotLight.ts

@@ -14,6 +14,19 @@
             return this.transformedPosition ? this.transformedPosition : this.position;
         }
 
+        public setShadowProjectionMatrix(matrix: Matrix, viewMatrix: Matrix, renderList: Array<AbstractMesh>): void {
+            var activeCamera = this.getScene().activeCamera;
+            Matrix.PerspectiveFovLHToRef(this.angle, 1.0, activeCamera.minZ, activeCamera.maxZ, matrix);
+        }
+
+        public supportsVSM(): boolean {
+            return true;
+        }
+
+        public needRefreshPerFrame(): boolean {
+            return false;
+        }
+
         public setDirectionToTarget(target: Vector3): Vector3 {
             this.direction = Vector3.Normalize(target.subtract(this.position));
             return this.direction;

+ 45 - 28
Babylon/Loading/Plugins/babylon.babylonFileLoader.js

@@ -688,36 +688,47 @@ var BABYLON;
                 return parseFloat(values[0]);
             };
             // traverse graph per trigger
-            var traverse = function (parsedAction, trigger, condition, action) {
+            var traverse = function (parsedAction, trigger, condition, action, combineArray) {
+                if (combineArray === void 0) { combineArray = null; }
                 if (parsedAction.detached)
                     return;
                 var parameters = new Array();
                 var target = null;
                 var propertyPath = null;
+                var combine = parsedAction.combine && parsedAction.combine.length > 0;
                 // Parameters
                 if (parsedAction.type === 2)
                     parameters.push(actionManager);
                 else
                     parameters.push(trigger);
-                for (var i = 0; i < parsedAction.properties.length; i++) {
-                    var value = parsedAction.properties[i].value;
-                    var name = parsedAction.properties[i].name;
-                    if (name === "target")
-                        value = target = scene.getNodeByName(value);
-                    else if (name === "parent")
-                        value = scene.getNodeByName(value);
-                    else if (name === "sound")
-                        value = scene.getSoundByName(value);
-                    else if (name !== "propertyPath") {
-                        if (parsedAction.type === 2 && name === "operator")
-                            value = BABYLON.ValueCondition[value];
-                        else
-                            value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
+                if (combine) {
+                    var actions = new Array();
+                    for (var j = 0; j < parsedAction.combine.length; j++) {
+                        traverse(parsedAction.combine[j], BABYLON.ActionManager.NothingTrigger, condition, action, actions);
                     }
-                    else {
-                        propertyPath = value;
+                    parameters.push(actions);
+                }
+                else {
+                    for (var i = 0; i < parsedAction.properties.length; i++) {
+                        var value = parsedAction.properties[i].value;
+                        var name = parsedAction.properties[i].name;
+                        if (name === "target")
+                            value = target = scene.getNodeByName(value);
+                        else if (name === "parent")
+                            value = scene.getNodeByName(value);
+                        else if (name === "sound")
+                            value = scene.getSoundByName(value);
+                        else if (name !== "propertyPath") {
+                            if (parsedAction.type === 2 && name === "operator")
+                                value = BABYLON.ValueCondition[value];
+                            else
+                                value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
+                        }
+                        else {
+                            propertyPath = value;
+                        }
+                        parameters.push(value);
                     }
-                    parameters.push(value);
                 }
                 parameters.push(condition);
                 // If interpolate value action
@@ -726,21 +737,27 @@ var BABYLON;
                     parameters[parameters.length - 1] = param;
                     parameters[parameters.length - 2] = condition;
                 }
-                // Action or condition(s)
+                // Action or condition(s) and not CombineAction
                 var newAction = instanciate(parsedAction.name, parameters);
-                if (newAction instanceof BABYLON.Condition) {
-                    condition = newAction;
-                    newAction = action;
+                if (combineArray === null) {
+                    if (newAction instanceof BABYLON.Condition) {
+                        condition = newAction;
+                        newAction = action;
+                    }
+                    else {
+                        condition = null;
+                        if (action)
+                            action.then(newAction);
+                        else
+                            actionManager.registerAction(newAction);
+                    }
                 }
                 else {
-                    condition = null;
-                    if (action)
-                        action.then(newAction);
-                    else
-                        actionManager.registerAction(newAction);
+                    if (combineArray !== null)
+                        combineArray.push(newAction);
                 }
                 for (var i = 0; i < parsedAction.children.length; i++)
-                    traverse(parsedAction.children[i], trigger, condition, newAction);
+                    traverse(parsedAction.children[i], trigger, condition, newAction, null);
             };
             for (var i = 0; i < parsedActions.children.length; i++) {
                 var triggerParams;

+ 46 - 30
Babylon/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -870,13 +870,14 @@
         };
 
         // traverse graph per trigger
-        var traverse = (parsedAction: any, trigger: any, condition: Condition, action: Action) => {
+        var traverse = (parsedAction: any, trigger: any, condition: Condition, action: Action, combineArray: Array<Action> = null) => {
             if (parsedAction.detached)
                 return;
 
             var parameters = new Array<any>();
             var target: any = null;
             var propertyPath: string = null;
+            var combine = parsedAction.combine && parsedAction.combine.length > 0;
 
             // Parameters
             if (parsedAction.type === 2)
@@ -884,26 +885,35 @@
             else
                 parameters.push(trigger);
 
-            for (var i = 0; i < parsedAction.properties.length; i++) {
-                var value = parsedAction.properties[i].value;
-                var name = parsedAction.properties[i].name;
-
-                if (name === "target")
-                    value = target = scene.getNodeByName(value);
-                else if (name === "parent")
-                    value = scene.getNodeByName(value);
-                else if (name === "sound")
-                    value = scene.getSoundByName(value);
-                else if (name !== "propertyPath") {
-                    if (parsedAction.type === 2 && name === "operator")
-                        value = BABYLON.ValueCondition[value];
-                    else
-                        value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
-                } else {
-                    propertyPath = value;
+            if (combine) {
+                var actions = new Array<Action>();
+                for (var j = 0; j < parsedAction.combine.length; j++) {
+                    traverse(parsedAction.combine[j], ActionManager.NothingTrigger, condition, action, actions);
                 }
+                parameters.push(actions);
+            }
+            else {
+                for (var i = 0; i < parsedAction.properties.length; i++) {
+                    var value = parsedAction.properties[i].value;
+                    var name = parsedAction.properties[i].name;
+
+                    if (name === "target")
+                        value = target = scene.getNodeByName(value);
+                    else if (name === "parent")
+                        value = scene.getNodeByName(value);
+                    else if (name === "sound")
+                        value = scene.getSoundByName(value);
+                    else if (name !== "propertyPath") {
+                        if (parsedAction.type === 2 && name === "operator")
+                            value = BABYLON.ValueCondition[value];
+                        else
+                            value = parseParameter(name, value, target, name === "value" ? propertyPath : null);
+                    } else {
+                        propertyPath = value;
+                    }
 
-                parameters.push(value);
+                    parameters.push(value);
+                }
             }
             parameters.push(condition);
 
@@ -914,21 +924,27 @@
                 parameters[parameters.length - 2] = condition;
             }
 
-            // Action or condition(s)
+            // Action or condition(s) and not CombineAction
             var newAction = instanciate(parsedAction.name, parameters);
-            if (newAction instanceof BABYLON.Condition) {
-                condition = newAction;
-                newAction = action;
-            } else {
-                condition = null;
-                if (action)
-                    action.then(newAction);
-                else
-                    actionManager.registerAction(newAction);
+            if (combineArray === null) {
+                if (newAction instanceof BABYLON.Condition) {
+                    condition = newAction;
+                    newAction = action;
+                } else {
+                    condition = null;
+                    if (action)
+                        action.then(newAction);
+                    else
+                        actionManager.registerAction(newAction);
+                }
+            }
+            else {
+                if (combineArray !== null)
+                    combineArray.push(newAction);
             }
 
             for (var i = 0; i < parsedAction.children.length; i++)
-                traverse(parsedAction.children[i], trigger, condition, newAction);
+                traverse(parsedAction.children[i], trigger, condition, newAction, null);
         };
 
         // triggers

+ 5 - 1
Babylon/Materials/Textures/babylon.renderTargetTexture.js

@@ -78,7 +78,7 @@ var BABYLON;
             this.releaseInternalTexture();
             this._texture = this.getScene().getEngine().createRenderTargetTexture(size, generateMipMaps);
         };
-        RenderTargetTexture.prototype.render = function (useCameraPostProcess) {
+        RenderTargetTexture.prototype.render = function (useCameraPostProcess, dumpForDebug) {
             var scene = this.getScene();
             var engine = scene.getEngine();
             if (this._waitingRenderList) {
@@ -135,6 +135,10 @@ var BABYLON;
             if (this.onAfterRender) {
                 this.onAfterRender();
             }
+            // Dump ?
+            if (dumpForDebug) {
+                BABYLON.Tools.DumpFramebuffer(this._size, this._size, engine);
+            }
             // Unbind
             engine.unBindFramebuffer(this._texture);
         };

+ 6 - 1
Babylon/Materials/Textures/babylon.renderTargetTexture.ts

@@ -87,7 +87,7 @@
             this._texture = this.getScene().getEngine().createRenderTargetTexture(size, generateMipMaps);
         }
 
-        public render(useCameraPostProcess?: boolean) {
+        public render(useCameraPostProcess?: boolean, dumpForDebug?: boolean) {
             var scene = this.getScene();
             var engine = scene.getEngine();
 
@@ -162,6 +162,11 @@
                 this.onAfterRender();
             }
 
+            // Dump ?
+            if (dumpForDebug) {
+                Tools.DumpFramebuffer(this._size, this._size, engine);
+            }
+
             // Unbind
             engine.unBindFramebuffer(this._texture);
         }

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
Babylon/Materials/babylon.standardMaterial.js


+ 2 - 2
Babylon/Materials/babylon.standardMaterial.ts

@@ -397,7 +397,7 @@
                         "vDiffuseInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vEmissiveInfos", "vSpecularInfos", "vBumpInfos",
                         "mBones",
                         "vClipPlane", "diffuseMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "specularMatrix", "bumpMatrix",
-                        "darkness0", "darkness1", "darkness2", "darkness3",
+                        "shadowsInfo0", "shadowsInfo1", "shadowsInfo2", "shadowsInfo3",
                         "diffuseLeftColor", "diffuseRightColor", "opacityParts", "reflectionLeftColor", "reflectionRightColor", "emissiveLeftColor", "emissiveRightColor"
                     ],
                     ["diffuseSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "specularSampler", "bumpSampler",
@@ -585,7 +585,7 @@
                         if (mesh.receiveShadows && shadowGenerator) {
                             this._effect.setMatrix("lightMatrix" + lightIndex, shadowGenerator.getTransformMatrix());
                             this._effect.setTexture("shadowSampler" + lightIndex, shadowGenerator.getShadowMap());
-                            this._effect.setFloat("darkness" + lightIndex, shadowGenerator.getDarkness());
+                            this._effect.setFloat3("shadowsInfo" + lightIndex, shadowGenerator.getDarkness(), shadowGenerator.getShadowMap().getSize().width, shadowGenerator.getBias());
                         }
                     }
 

+ 6 - 1
Babylon/Math/babylon.math.js

@@ -1709,11 +1709,16 @@ var BABYLON;
             return Matrix.FromValuesToRef(this._xAxis.x, this._yAxis.x, this._zAxis.x, 0, this._xAxis.y, this._yAxis.y, this._zAxis.y, 0, this._xAxis.z, this._yAxis.z, this._zAxis.z, 0, ex, ey, ez, 1, result);
         };
         Matrix.OrthoLH = function (width, height, znear, zfar) {
+            var matrix = Matrix.Zero();
+            Matrix.OrthoLHToRef(width, height, znear, zfar, matrix);
+            return matrix;
+        };
+        Matrix.OrthoLHToRef = function (width, height, znear, zfar, result) {
             var hw = 2.0 / width;
             var hh = 2.0 / height;
             var id = 1.0 / (zfar - znear);
             var nid = znear / (znear - zfar);
-            return Matrix.FromValues(hw, 0, 0, 0, 0, hh, 0, 0, 0, 0, id, 0, 0, 0, nid, 1);
+            Matrix.FromValuesToRef(hw, 0, 0, 0, 0, hh, 0, 0, 0, 0, id, 0, 0, 0, nid, 1, result);
         };
         Matrix.OrthoOffCenterLH = function (left, right, bottom, top, znear, zfar) {
             var matrix = Matrix.Zero();

+ 10 - 2
Babylon/Math/babylon.math.ts

@@ -2166,15 +2166,23 @@
         }
 
         public static OrthoLH(width: number, height: number, znear: number, zfar: number): Matrix {
+            var matrix = Matrix.Zero();
+
+            Matrix.OrthoLHToRef(width, height, znear, zfar, matrix);
+
+            return matrix;
+        }
+
+        public static OrthoLHToRef(width: number, height: number, znear: number, zfar: number, result: Matrix): void {
             var hw = 2.0 / width;
             var hh = 2.0 / height;
             var id = 1.0 / (zfar - znear);
             var nid = znear / (znear - zfar);
 
-            return Matrix.FromValues(hw, 0, 0, 0,
+            Matrix.FromValuesToRef(hw, 0, 0, 0,
                 0, hh, 0, 0,
                 0, 0, id, 0,
-                0, 0, nid, 1);
+                0, 0, nid, 1, result);
         }
 
         public static OrthoOffCenterLH(left: number, right: number, bottom: number, top: number, znear: number, zfar: number): Matrix {

+ 24 - 24
Babylon/Shaders/default.fragment.fx

@@ -35,7 +35,7 @@ uniform vec3 vLightSpecular0;
 #ifdef SHADOW0
 varying vec4 vPositionFromLight0;
 uniform sampler2D shadowSampler0;
-uniform float darkness0;
+uniform vec3 shadowsInfo0;
 #endif
 #ifdef SPOTLIGHT0
 uniform vec4 vLightDirection0;
@@ -52,7 +52,7 @@ uniform vec3 vLightSpecular1;
 #ifdef SHADOW1
 varying vec4 vPositionFromLight1;
 uniform sampler2D shadowSampler1;
-uniform float darkness1;
+uniform vec3 shadowsInfo1;
 #endif
 #ifdef SPOTLIGHT1
 uniform vec4 vLightDirection1;
@@ -69,7 +69,7 @@ uniform vec3 vLightSpecular2;
 #ifdef SHADOW2
 varying vec4 vPositionFromLight2;
 uniform sampler2D shadowSampler2;
-uniform float darkness2;
+uniform vec3 shadowsInfo2;
 #endif
 #ifdef SPOTLIGHT2
 uniform vec4 vLightDirection2;
@@ -86,7 +86,7 @@ uniform vec3 vLightSpecular3;
 #ifdef SHADOW3
 varying vec4 vPositionFromLight3;
 uniform sampler2D shadowSampler3;
-uniform float darkness3;
+uniform vec3 shadowsInfo3;
 #endif
 #ifdef SPOTLIGHT3
 uniform vec4 vLightDirection3;
@@ -213,7 +213,7 @@ float unpackHalf(vec2 color)
 	return color.x + (color.y / 255.0);
 }
 
-float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness)
+float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float darkness, float bias)
 {
 	vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
 	vec2 uv = 0.5 * depth.xy + vec2(0.5, 0.5);
@@ -223,7 +223,7 @@ float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float dark
 		return 1.0;
 	}
 
-	float shadow = unpack(texture2D(shadowSampler, uv));
+	float shadow = unpack(texture2D(shadowSampler, uv)) + bias;
 
 	if (depth.z > shadow)
 	{
@@ -232,7 +232,7 @@ float computeShadow(vec4 vPositionFromLight, sampler2D shadowSampler, float dark
 	return 1.;
 }
 
-float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler)
+float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler, float mapSize, float bias)
 {
 	vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
 	vec2 uv = 0.5 * depth.xy + vec2(0.5, 0.5);
@@ -251,10 +251,10 @@ float computeShadowWithPCF(vec4 vPositionFromLight, sampler2D shadowSampler)
 	poissonDisk[3] = vec2(0.34495938, 0.29387760);
 
 	// Poisson Sampling
-	if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] / 1500.0))  <  depth.z) visibility -= 0.2;
-	if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] / 1500.0))  <  depth.z) visibility -= 0.2;
-	if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] / 1500.0))  <  depth.z) visibility -= 0.2;
-	if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] / 1500.0))  <  depth.z) visibility -= 0.2;
+	if (unpack(texture2D(shadowSampler, uv + poissonDisk[0] / mapSize)) + bias  <  depth.z) visibility -= 0.25;
+	if (unpack(texture2D(shadowSampler, uv + poissonDisk[1] / mapSize)) + bias  <  depth.z) visibility -= 0.25;
+	if (unpack(texture2D(shadowSampler, uv + poissonDisk[2] / mapSize)) + bias  <  depth.z) visibility -= 0.25;
+	if (unpack(texture2D(shadowSampler, uv + poissonDisk[3] / mapSize)) + bias  <  depth.z) visibility -= 0.25;
 
 	return visibility;
 }
@@ -268,10 +268,10 @@ float ChebychevInequality(vec2 moments, float t)
 	}
 
 	float variance = moments.y - (moments.x * moments.x);
-	variance = max(variance, 0.);
+	variance = max(variance, 0.02);
 
 	float d = t - moments.x;
-	return variance / (variance + d * d);
+	return clamp(variance / (variance + d * d) - 0.2, 0., 1.0);
 }
 
 float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler)
@@ -279,7 +279,7 @@ float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler)
 	vec3 depth = vPositionFromLight.xyz / vPositionFromLight.w;
 	vec2 uv = 0.5 * depth.xy + vec2(0.5, 0.5);
 
-	if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0)
+	if (uv.x < 0. || uv.x > 1.0 || uv.y < 0. || uv.y > 1.0 || depth.z > 1.0)
 	{
 		return 1.0;
 	}
@@ -287,7 +287,7 @@ float computeShadowWithVSM(vec4 vPositionFromLight, sampler2D shadowSampler)
 	vec4 texel = texture2D(shadowSampler, uv);
 
 	vec2 moments = vec2(unpackHalf(texel.xy), unpackHalf(texel.zw));
-	return clamp(1.3 - ChebychevInequality(moments, depth.z), 0., 1.0);
+	return 1.0 - ChebychevInequality(moments, depth.z);
 }
 #endif
 
@@ -420,7 +420,7 @@ lightingInfo computeSpotLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightDa
 	if (cosAngle >= lightDirection.w)
 	{
 		cosAngle = max(0., pow(cosAngle, lightData.w));
-		spotAtten = max(0., (cosAngle - lightDirection.w) / (1. - cosAngle));
+		spotAtten = clamp((cosAngle - lightDirection.w) / (1. - cosAngle), 0.0, 1.0);
 
 		// Diffuse
 		float ndl = max(0., dot(vNormal, -lightDirection.xyz));
@@ -534,9 +534,9 @@ void main(void) {
 	shadow = computeShadowWithVSM(vPositionFromLight0, shadowSampler0);
 #else
 	#ifdef SHADOWPCF0
-		shadow = computeShadowWithPCF(vPositionFromLight0, shadowSampler0);
+		shadow = computeShadowWithPCF(vPositionFromLight0, shadowSampler0, shadowsInfo0.y, shadowsInfo0.z);
 	#else
-		shadow = computeShadow(vPositionFromLight0, shadowSampler0, darkness0);
+		shadow = computeShadow(vPositionFromLight0, shadowSampler0, shadowsInfo0.x, shadowsInfo0.z);
 	#endif
 #endif
 #else
@@ -561,9 +561,9 @@ void main(void) {
 	shadow = computeShadowWithVSM(vPositionFromLight1, shadowSampler1);
 #else
 	#ifdef SHADOWPCF1
-		shadow = computeShadowWithPCF(vPositionFromLight1, shadowSampler1);
+		shadow = computeShadowWithPCF(vPositionFromLight1, shadowSampler1, shadowsInfo1.y, shadowsInfo1.z);
 	#else
-		shadow = computeShadow(vPositionFromLight1, shadowSampler1, darkness1);
+		shadow = computeShadow(vPositionFromLight1, shadowSampler1, shadowsInfo1.x, shadowsInfo1.z);
 	#endif
 #endif
 #else
@@ -588,9 +588,9 @@ void main(void) {
 	shadow = computeShadowWithVSM(vPositionFromLight2, shadowSampler2);
 #else
 	#ifdef SHADOWPCF2
-		shadow = computeShadowWithPCF(vPositionFromLight2, shadowSampler2);
+		shadow = computeShadowWithPCF(vPositionFromLight2, shadowSampler2, shadowsInfo2.y, shadowsInfo2.z);
 	#else
-		shadow = computeShadow(vPositionFromLight2, shadowSampler2, darkness2);
+		shadow = computeShadow(vPositionFromLight2, shadowSampler2, shadowsInfo2.x, shadowsInfo2.z);
 	#endif	
 #endif	
 #else
@@ -615,9 +615,9 @@ void main(void) {
 	shadow = computeShadowWithVSM(vPositionFromLight3, shadowSampler3);
 #else
 	#ifdef SHADOWPCF3
-		shadow = computeShadowWithPCF(vPositionFromLight3, shadowSampler3);
+		shadow = computeShadowWithPCF(vPositionFromLight3, shadowSampler3, shadowsInfo3.y, shadowsInfo3.z);
 	#else
-		shadow = computeShadow(vPositionFromLight3, shadowSampler3, darkness3);
+		shadow = computeShadow(vPositionFromLight3, shadowSampler3, shadowsInfo3.x, shadowsInfo3.z);
 	#endif	
 #endif	
 #else

+ 2 - 2
Babylon/Shaders/shadowMap.fragment.fx

@@ -7,7 +7,7 @@ vec4 pack(float depth)
 	const vec4 bitOffset = vec4(255. * 255. * 255., 255. * 255., 255., 1.);
 	const vec4 bitMask = vec4(0., 1. / 255., 1. / 255., 1. / 255.);
 	
-	vec4 comp = mod(depth * bitOffset * vec4(254.), vec4(255.)) / vec4(254.);
+	vec4 comp = mod(depth * bitOffset * vec4(255.), vec4(255.)) / vec4(255.);
 	comp -= comp.xxyz * bitMask;
 	
 	return comp;
@@ -39,7 +39,7 @@ void main(void)
 #endif
 
 #ifdef VSM
-	float moment1 = gl_FragCoord.z / gl_FragCoord.w;
+	float moment1 = gl_FragCoord.z;
 	float moment2 = moment1 * moment1;
 	gl_FragColor = vec4(packHalf(moment1), packHalf(moment2));
 #else

+ 2 - 3
Babylon/Shaders/shadowMap.vertex.fx

@@ -53,13 +53,12 @@ void main(void)
 	mat4 m2 = mBones[int(matricesIndices.z)] * matricesWeights.z;
 	mat4 m3 = mBones[int(matricesIndices.w)] * matricesWeights.w;
 	finalWorld = finalWorld * (m0 + m1 + m2 + m3);
-	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
-#else
+#endif
+
 #ifndef VSM
 	vPosition = viewProjection * finalWorld * vec4(position, 1.0);
 #endif
 	gl_Position = viewProjection * finalWorld * vec4(position, 1.0);
-#endif
 
 #ifdef ALPHATEST
 #ifdef UV1

+ 64 - 62
Babylon/Tools/babylon.filesInput.js

@@ -4,42 +4,42 @@ var BABYLON;
         /// Register to core BabylonJS object: engine, scene, rendering canvas, callback function when the scene will be loaded,
         /// loading progress callback and optionnal addionnal logic to call in the rendering loop
         function FilesInput(p_engine, p_scene, p_canvas, p_sceneLoadedCallback, p_progressCallback, p_additionnalRenderLoopLogicCallback, p_textureLoadingCallback, p_startingProcessingFilesCallback) {
-            this.engine = p_engine;
-            this.canvas = p_canvas;
-            this.currentScene = p_scene;
-            this.sceneLoadedCallback = p_sceneLoadedCallback;
-            this.progressCallback = p_progressCallback;
-            this.additionnalRenderLoopLogicCallback = p_additionnalRenderLoopLogicCallback;
-            this.textureLoadingCallback = p_textureLoadingCallback;
-            this.startingProcessingFilesCallback = p_startingProcessingFilesCallback;
+            this._engine = p_engine;
+            this._canvas = p_canvas;
+            this._currentScene = p_scene;
+            this._sceneLoadedCallback = p_sceneLoadedCallback;
+            this._progressCallback = p_progressCallback;
+            this._additionnalRenderLoopLogicCallback = p_additionnalRenderLoopLogicCallback;
+            this._textureLoadingCallback = p_textureLoadingCallback;
+            this._startingProcessingFilesCallback = p_startingProcessingFilesCallback;
         }
         FilesInput.prototype.monitorElementForDragNDrop = function (p_elementToMonitor) {
             var _this = this;
             if (p_elementToMonitor) {
-                this.elementToMonitor = p_elementToMonitor;
-                this.elementToMonitor.addEventListener("dragenter", function (e) {
+                this._elementToMonitor = p_elementToMonitor;
+                this._elementToMonitor.addEventListener("dragenter", function (e) {
                     _this.drag(e);
                 }, false);
-                this.elementToMonitor.addEventListener("dragover", function (e) {
+                this._elementToMonitor.addEventListener("dragover", function (e) {
                     _this.drag(e);
                 }, false);
-                this.elementToMonitor.addEventListener("drop", function (e) {
+                this._elementToMonitor.addEventListener("drop", function (e) {
                     _this.drop(e);
                 }, false);
             }
         };
         FilesInput.prototype.renderFunction = function () {
-            if (this.additionnalRenderLoopLogicCallback) {
-                this.additionnalRenderLoopLogicCallback();
+            if (this._additionnalRenderLoopLogicCallback) {
+                this._additionnalRenderLoopLogicCallback();
             }
-            if (this.currentScene) {
-                if (this.textureLoadingCallback) {
-                    var remaining = this.currentScene.getWaitingItemsCount();
+            if (this._currentScene) {
+                if (this._textureLoadingCallback) {
+                    var remaining = this._currentScene.getWaitingItemsCount();
                     if (remaining > 0) {
-                        this.textureLoadingCallback(remaining);
+                        this._textureLoadingCallback(remaining);
                     }
                 }
-                this.currentScene.render();
+                this._currentScene.render();
             }
         };
         FilesInput.prototype.drag = function (e) {
@@ -52,27 +52,23 @@ var BABYLON;
             this.loadFiles(eventDrop);
         };
         FilesInput.prototype.loadFiles = function (event) {
-            var _this = this;
-            var that = this;
-            if (this.startingProcessingFilesCallback)
-                this.startingProcessingFilesCallback();
-            var sceneFileToLoad;
-            var filesToLoad;
+            if (this._startingProcessingFilesCallback)
+                this._startingProcessingFilesCallback();
             // Handling data transfer via drag'n'drop
             if (event && event.dataTransfer && event.dataTransfer.files) {
-                filesToLoad = event.dataTransfer.files;
+                this._filesToLoad = event.dataTransfer.files;
             }
             // Handling files from input files
             if (event && event.target && event.target.files) {
-                filesToLoad = event.target.files;
+                this._filesToLoad = event.target.files;
             }
-            if (filesToLoad && filesToLoad.length > 0) {
-                for (var i = 0; i < filesToLoad.length; i++) {
-                    switch (filesToLoad[i].type) {
+            if (this._filesToLoad && this._filesToLoad.length > 0) {
+                for (var i = 0; i < this._filesToLoad.length; i++) {
+                    switch (this._filesToLoad[i].type) {
                         case "image/jpeg":
                         case "image/png":
                         case "image/bmp":
-                            FilesInput.FilesTextures[filesToLoad[i].name] = filesToLoad[i];
+                            FilesInput.FilesTextures[this._filesToLoad[i].name] = this._filesToLoad[i];
                             break;
                         case "image/targa":
                         case "image/vnd.ms-dds":
@@ -83,45 +79,51 @@ var BABYLON;
                         case "audio/mpeg3":
                         case "audio/x-mpeg-3":
                         case "audio/ogg":
-                            FilesInput.FilesToLoad[filesToLoad[i].name] = filesToLoad[i];
+                            FilesInput.FilesToLoad[this._filesToLoad[i].name] = this._filesToLoad[i];
                             break;
                         default:
-                            if (filesToLoad[i].name.indexOf(".babylon") !== -1 && filesToLoad[i].name.indexOf(".manifest") === -1 && filesToLoad[i].name.indexOf(".incremental") === -1 && filesToLoad[i].name.indexOf(".babylonmeshdata") === -1 && filesToLoad[i].name.indexOf(".babylongeometrydata") === -1) {
-                                sceneFileToLoad = filesToLoad[i];
+                            if (this._filesToLoad[i].name.indexOf(".babylon") !== -1 && this._filesToLoad[i].name.indexOf(".manifest") === -1 && this._filesToLoad[i].name.indexOf(".incremental") === -1 && this._filesToLoad[i].name.indexOf(".babylonmeshdata") === -1 && this._filesToLoad[i].name.indexOf(".babylongeometrydata") === -1) {
+                                this._sceneFileToLoad = this._filesToLoad[i];
                             }
                             break;
                     }
                 }
-                // If a ".babylon" file has been provided
-                if (sceneFileToLoad) {
-                    if (this.currentScene) {
-                        this.engine.stopRenderLoop();
-                        this.currentScene.dispose();
-                    }
-                    BABYLON.SceneLoader.Load("file:", sceneFileToLoad, this.engine, function (newScene) {
-                        that.currentScene = newScene;
-                        // Wait for textures and shaders to be ready
-                        that.currentScene.executeWhenReady(function () {
-                            // Attach camera to canvas inputs
-                            if (that.currentScene.activeCamera) {
-                                that.currentScene.activeCamera.attachControl(that.canvas);
-                            }
-                            if (that.sceneLoadedCallback) {
-                                that.sceneLoadedCallback(sceneFileToLoad, that.currentScene);
-                            }
-                            that.engine.runRenderLoop(function () {
-                                that.renderFunction();
-                            });
-                        });
-                    }, function (progress) {
-                        if (_this.progressCallback) {
-                            _this.progressCallback(progress);
+                this.reload();
+            }
+        };
+        FilesInput.prototype.reload = function () {
+            var _this = this;
+            var that = this;
+            // If a ".babylon" file has been provided
+            if (this._sceneFileToLoad) {
+                if (this._currentScene) {
+                    this._engine.stopRenderLoop();
+                    this._currentScene.dispose();
+                }
+                BABYLON.SceneLoader.Load("file:", this._sceneFileToLoad, this._engine, function (newScene) {
+                    that._currentScene = newScene;
+                    // Wait for textures and shaders to be ready
+                    that._currentScene.executeWhenReady(function () {
+                        // Attach camera to canvas inputs
+                        if (!that._currentScene.activeCamera || that._currentScene.lights.length === 0) {
+                            that._currentScene.createDefaultCameraOrLight();
                         }
+                        that._currentScene.activeCamera.attachControl(that._canvas);
+                        if (that._sceneLoadedCallback) {
+                            that._sceneLoadedCallback(_this._sceneFileToLoad, that._currentScene);
+                        }
+                        that._engine.runRenderLoop(function () {
+                            that.renderFunction();
+                        });
                     });
-                }
-                else {
-                    BABYLON.Tools.Error("Please provide a valid .babylon file.");
-                }
+                }, function (progress) {
+                    if (_this._progressCallback) {
+                        _this._progressCallback(progress);
+                    }
+                });
+            }
+            else {
+                BABYLON.Tools.Error("Please provide a valid .babylon file.");
             }
         };
         FilesInput.FilesTextures = new Array();

+ 77 - 71
Babylon/Tools/babylon.filesInput.ts

@@ -1,54 +1,57 @@
 module BABYLON {
     export class FilesInput {
-        private engine: Engine;
-        private currentScene: Scene;
-        private canvas: HTMLCanvasElement;
-        private sceneLoadedCallback;
-        private progressCallback;
-        private additionnalRenderLoopLogicCallback;
-        private textureLoadingCallback;
-        private startingProcessingFilesCallback;
-        private elementToMonitor: HTMLElement;
+        private _engine: Engine;
+        private _currentScene: Scene;
+        private _canvas: HTMLCanvasElement;
+        private _sceneLoadedCallback;
+        private _progressCallback;
+        private _additionnalRenderLoopLogicCallback;
+        private _textureLoadingCallback;
+        private _startingProcessingFilesCallback;
+        private _elementToMonitor: HTMLElement;
         public static FilesTextures: any[] = new Array();
         public static FilesToLoad: any[] = new Array();
 
+        private _sceneFileToLoad: File;
+        private _filesToLoad: File[];
+
         /// Register to core BabylonJS object: engine, scene, rendering canvas, callback function when the scene will be loaded,
         /// loading progress callback and optionnal addionnal logic to call in the rendering loop
         constructor(p_engine: Engine, p_scene: Scene, p_canvas: HTMLCanvasElement, p_sceneLoadedCallback,
             p_progressCallback, p_additionnalRenderLoopLogicCallback, p_textureLoadingCallback, p_startingProcessingFilesCallback) {
-            this.engine = p_engine;
-            this.canvas = p_canvas;
-            this.currentScene = p_scene;
-            this.sceneLoadedCallback = p_sceneLoadedCallback;
-            this.progressCallback = p_progressCallback;
-            this.additionnalRenderLoopLogicCallback = p_additionnalRenderLoopLogicCallback;
-            this.textureLoadingCallback = p_textureLoadingCallback;
-            this.startingProcessingFilesCallback = p_startingProcessingFilesCallback;
+            this._engine = p_engine;
+            this._canvas = p_canvas;
+            this._currentScene = p_scene;
+            this._sceneLoadedCallback = p_sceneLoadedCallback;
+            this._progressCallback = p_progressCallback;
+            this._additionnalRenderLoopLogicCallback = p_additionnalRenderLoopLogicCallback;
+            this._textureLoadingCallback = p_textureLoadingCallback;
+            this._startingProcessingFilesCallback = p_startingProcessingFilesCallback;
         }
 
         public monitorElementForDragNDrop(p_elementToMonitor: HTMLElement): void {
             if (p_elementToMonitor) {
-                this.elementToMonitor = p_elementToMonitor;
-                this.elementToMonitor.addEventListener("dragenter", (e) => { this.drag(e); }, false);
-                this.elementToMonitor.addEventListener("dragover", (e) => { this.drag(e); }, false);
-                this.elementToMonitor.addEventListener("drop", (e) => { this.drop(e); }, false);
+                this._elementToMonitor = p_elementToMonitor;
+                this._elementToMonitor.addEventListener("dragenter", (e) => { this.drag(e); }, false);
+                this._elementToMonitor.addEventListener("dragover", (e) => { this.drag(e); }, false);
+                this._elementToMonitor.addEventListener("drop", (e) => { this.drop(e); }, false);
             }
         }
 
         private renderFunction(): void {
-            if (this.additionnalRenderLoopLogicCallback) {
-                this.additionnalRenderLoopLogicCallback();
+            if (this._additionnalRenderLoopLogicCallback) {
+                this._additionnalRenderLoopLogicCallback();
             }
 
-            if (this.currentScene) {
-                if (this.textureLoadingCallback) {
-                    var remaining = this.currentScene.getWaitingItemsCount();
+            if (this._currentScene) {
+                if (this._textureLoadingCallback) {
+                    var remaining = this._currentScene.getWaitingItemsCount();
 
                     if (remaining > 0) {
-                        this.textureLoadingCallback(remaining);
+                        this._textureLoadingCallback(remaining);
                     }
                 }
-                this.currentScene.render();
+                this._currentScene.render();
             }
         }
 
@@ -64,30 +67,26 @@
             this.loadFiles(eventDrop);
         }
 
-        private loadFiles(event): void {
-            var that = this;
-            if (this.startingProcessingFilesCallback) this.startingProcessingFilesCallback();
-
-            var sceneFileToLoad: File;
-            var filesToLoad: File[];
+        public loadFiles(event): void {
+            if (this._startingProcessingFilesCallback) this._startingProcessingFilesCallback();
 
             // Handling data transfer via drag'n'drop
             if (event && event.dataTransfer && event.dataTransfer.files) {
-                filesToLoad = event.dataTransfer.files;
+                this._filesToLoad = event.dataTransfer.files;
             }
 
             // Handling files from input files
             if (event && event.target && event.target.files) {
-                filesToLoad = event.target.files;
+                this._filesToLoad = event.target.files;
             }
 
-            if (filesToLoad && filesToLoad.length > 0) {
-                for (var i = 0; i < filesToLoad.length; i++) {
-                    switch (filesToLoad[i].type) {
+            if (this._filesToLoad && this._filesToLoad.length > 0) {
+                for (var i = 0; i < this._filesToLoad.length; i++) {
+                    switch (this._filesToLoad[i].type) {
                         case "image/jpeg":
                         case "image/png":
                         case "image/bmp":
-                            FilesInput.FilesTextures[filesToLoad[i].name] = filesToLoad[i];
+                            FilesInput.FilesTextures[this._filesToLoad[i].name] = this._filesToLoad[i];
                             break;
                         case "image/targa":
                         case "image/vnd.ms-dds":
@@ -98,48 +97,55 @@
                         case "audio/mpeg3":
                         case "audio/x-mpeg-3":
                         case "audio/ogg":
-                            FilesInput.FilesToLoad[filesToLoad[i].name] = filesToLoad[i];
+                            FilesInput.FilesToLoad[this._filesToLoad[i].name] = this._filesToLoad[i];
                             break;
                         default:
-                            if (filesToLoad[i].name.indexOf(".babylon") !== -1 && filesToLoad[i].name.indexOf(".manifest") === -1
-                                && filesToLoad[i].name.indexOf(".incremental") === -1 && filesToLoad[i].name.indexOf(".babylonmeshdata") === -1
-                                && filesToLoad[i].name.indexOf(".babylongeometrydata") === -1) {
-                                sceneFileToLoad = filesToLoad[i];
+                            if (this._filesToLoad[i].name.indexOf(".babylon") !== -1 && this._filesToLoad[i].name.indexOf(".manifest") === -1
+                                && this._filesToLoad[i].name.indexOf(".incremental") === -1 && this._filesToLoad[i].name.indexOf(".babylonmeshdata") === -1
+                                && this._filesToLoad[i].name.indexOf(".babylongeometrydata") === -1) {
+                                this._sceneFileToLoad = this._filesToLoad[i];
                             }
                             break;
                     }
                 }
 
-                // If a ".babylon" file has been provided
-                if (sceneFileToLoad) {
-                    if (this.currentScene) {
-                        this.engine.stopRenderLoop();
-                        this.currentScene.dispose();
-                    }
+                this.reload();
+            }
+        }
 
-                    SceneLoader.Load("file:", sceneFileToLoad, this.engine, (newScene) => {
-                        that.currentScene = newScene;
+        public reload() {
+            var that = this;
+            // If a ".babylon" file has been provided
+            if (this._sceneFileToLoad) {
+                if (this._currentScene) {
+                    this._engine.stopRenderLoop();
+                    this._currentScene.dispose();
+                }
 
-                        // Wait for textures and shaders to be ready
-                        that.currentScene.executeWhenReady(() => {
-                            // Attach camera to canvas inputs
-                            if (that.currentScene.activeCamera) {
-                                that.currentScene.activeCamera.attachControl(that.canvas);
-                            }
-                            if (that.sceneLoadedCallback) {
-                                that.sceneLoadedCallback(sceneFileToLoad, that.currentScene);
-                            }
-                            that.engine.runRenderLoop(() => { that.renderFunction(); });
-                        });
-                    }, progress => {
-                        if (this.progressCallback) {
-                            this.progressCallback(progress);
+                SceneLoader.Load("file:", this._sceneFileToLoad, this._engine, (newScene) => {
+                    that._currentScene = newScene;
+
+                    // Wait for textures and shaders to be ready
+                    that._currentScene.executeWhenReady(() => {
+                        // Attach camera to canvas inputs
+                        if (!that._currentScene.activeCamera || that._currentScene.lights.length === 0) {     
+                            that._currentScene.createDefaultCameraOrLight();
                         }
+                        that._currentScene.activeCamera.attachControl(that._canvas);
+
+                        if (that._sceneLoadedCallback) {
+                            that._sceneLoadedCallback(this._sceneFileToLoad, that._currentScene);
+                        }
+                        that._engine.runRenderLoop(() => { that.renderFunction(); });
                     });
-                }
-                else {
-                    Tools.Error("Please provide a valid .babylon file.");
-                }
+                }, progress => {
+                        if (this._progressCallback) {
+                            this._progressCallback(progress);
+                        }
+                    });
+            }
+            else {
+                Tools.Error("Please provide a valid .babylon file.");
             }
         }
     }

+ 55 - 48
Babylon/Tools/babylon.tools.js

@@ -228,6 +228,10 @@ var BABYLON;
         };
         Tools.ReadFile = function (fileToLoad, callback, progressCallBack, useArrayBuffer) {
             var reader = new FileReader();
+            reader.onerror = function (e) {
+                Tools.Log("Error while reading file: " + fileToLoad.name);
+                callback(JSON.stringify({ autoClear: true, clearColor: [1, 0, 0], ambientColor: [0, 0, 0], gravity: [0, -9.81, 0], meshes: [], cameras: [], lights: [] }));
+            };
             reader.onload = function (e) {
                 //target doesn't have result from ts 1.3
                 callback(e.target['result']);
@@ -349,6 +353,56 @@ var BABYLON;
                 }
             }
         };
+        Tools.DumpFramebuffer = function (width, height, engine) {
+            // Read the contents of the framebuffer
+            var numberOfChannelsByLine = width * 4;
+            var halfHeight = height / 2;
+            //Reading datas from WebGL
+            var data = engine.readPixels(0, 0, width, height);
+            for (var i = 0; i < halfHeight; i++) {
+                for (var j = 0; j < numberOfChannelsByLine; j++) {
+                    var currentCell = j + i * numberOfChannelsByLine;
+                    var targetLine = height - i - 1;
+                    var targetCell = j + targetLine * numberOfChannelsByLine;
+                    var temp = data[currentCell];
+                    data[currentCell] = data[targetCell];
+                    data[targetCell] = temp;
+                }
+            }
+            // Create a 2D canvas to store the result
+            if (!screenshotCanvas) {
+                screenshotCanvas = document.createElement('canvas');
+            }
+            screenshotCanvas.width = width;
+            screenshotCanvas.height = height;
+            var context = screenshotCanvas.getContext('2d');
+            // Copy the pixels to a 2D canvas
+            var imageData = context.createImageData(width, height);
+            //cast is due to ts error in lib.d.ts, see here - https://github.com/Microsoft/TypeScript/issues/949
+            var castData = imageData.data;
+            castData.set(data);
+            context.putImageData(imageData, 0, 0);
+            var base64Image = screenshotCanvas.toDataURL();
+            //Creating a link if the browser have the download attribute on the a tag, to automatically start download generated image.
+            if (("download" in document.createElement("a"))) {
+                var a = window.document.createElement("a");
+                a.href = base64Image;
+                var date = new Date();
+                var stringDate = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate() + "-" + date.getHours() + ":" + date.getMinutes();
+                a.setAttribute("download", "screenshot-" + stringDate + ".png");
+                window.document.body.appendChild(a);
+                a.addEventListener("click", function () {
+                    a.parentElement.removeChild(a);
+                });
+                a.click();
+            }
+            else {
+                var newWindow = window.open("");
+                var img = newWindow.document.createElement("img");
+                img.src = base64Image;
+                newWindow.document.body.appendChild(img);
+            }
+        };
         Tools.CreateScreenshot = function (engine, camera, size) {
             var width;
             var height;
@@ -390,54 +444,7 @@ var BABYLON;
             var texture = new BABYLON.RenderTargetTexture("screenShot", size, scene, false, false);
             texture.renderList = scene.meshes;
             texture.onAfterRender = function () {
-                // Read the contents of the framebuffer
-                var numberOfChannelsByLine = width * 4;
-                var halfHeight = height / 2;
-                //Reading datas from WebGL
-                var data = engine.readPixels(0, 0, width, height);
-                for (var i = 0; i < halfHeight; i++) {
-                    for (var j = 0; j < numberOfChannelsByLine; j++) {
-                        var currentCell = j + i * numberOfChannelsByLine;
-                        var targetLine = height - i - 1;
-                        var targetCell = j + targetLine * numberOfChannelsByLine;
-                        var temp = data[currentCell];
-                        data[currentCell] = data[targetCell];
-                        data[targetCell] = temp;
-                    }
-                }
-                // Create a 2D canvas to store the result
-                if (!screenshotCanvas) {
-                    screenshotCanvas = document.createElement('canvas');
-                }
-                screenshotCanvas.width = width;
-                screenshotCanvas.height = height;
-                var context = screenshotCanvas.getContext('2d');
-                // Copy the pixels to a 2D canvas
-                var imageData = context.createImageData(width, height);
-                //cast is due to ts error in lib.d.ts, see here - https://github.com/Microsoft/TypeScript/issues/949
-                var data = imageData.data;
-                data.set(data);
-                context.putImageData(imageData, 0, 0);
-                var base64Image = screenshotCanvas.toDataURL();
-                //Creating a link if the browser have the download attribute on the a tag, to automatically start download generated image.
-                if (("download" in document.createElement("a"))) {
-                    var a = window.document.createElement("a");
-                    a.href = base64Image;
-                    var date = new Date();
-                    var stringDate = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate() + "-" + date.getHours() + ":" + date.getMinutes();
-                    a.setAttribute("download", "screenshot-" + stringDate + ".png");
-                    window.document.body.appendChild(a);
-                    a.addEventListener("click", function () {
-                        a.parentElement.removeChild(a);
-                    });
-                    a.click();
-                }
-                else {
-                    var newWindow = window.open("");
-                    var img = newWindow.document.createElement("img");
-                    img.src = base64Image;
-                    newWindow.document.body.appendChild(img);
-                }
+                Tools.DumpFramebuffer(width, height, engine);
             };
             scene.incrementRenderId();
             texture.render(true);

+ 67 - 60
Babylon/Tools/babylon.tools.ts

@@ -294,6 +294,10 @@
 
         public static ReadFile(fileToLoad, callback, progressCallBack, useArrayBuffer?: boolean): void {
             var reader = new FileReader();
+            reader.onerror = e => {
+                Tools.Log("Error while reading file: " + fileToLoad.name);
+                callback(JSON.stringify({ autoClear: true, clearColor: [1, 0, 0], ambientColor: [0, 0, 0], gravity: [0, -9.81, 0], meshes: [], cameras: [], lights: []}));
+            };
             reader.onload = e => {
                 //target doesn't have result from ts 1.3
                 callback(e.target['result']);
@@ -429,6 +433,68 @@
             }
         }
 
+        public static DumpFramebuffer(width: number, height: number, engine: Engine): void {
+            // Read the contents of the framebuffer
+            var numberOfChannelsByLine = width * 4;
+            var halfHeight = height / 2;
+
+            //Reading datas from WebGL
+            var data = engine.readPixels(0, 0, width, height);
+
+            //To flip image on Y axis.
+            for (var i = 0; i < halfHeight; i++) {
+                for (var j = 0; j < numberOfChannelsByLine; j++) {
+                    var currentCell = j + i * numberOfChannelsByLine;
+                    var targetLine = height - i - 1;
+                    var targetCell = j + targetLine * numberOfChannelsByLine;
+
+                    var temp = data[currentCell];
+                    data[currentCell] = data[targetCell];
+                    data[targetCell] = temp;
+                }
+            }
+
+            // Create a 2D canvas to store the result
+            if (!screenshotCanvas) {
+                screenshotCanvas = document.createElement('canvas');
+            }
+            screenshotCanvas.width = width;
+            screenshotCanvas.height = height;
+            var context = screenshotCanvas.getContext('2d');
+
+            // Copy the pixels to a 2D canvas
+            var imageData = context.createImageData(width, height);
+            //cast is due to ts error in lib.d.ts, see here - https://github.com/Microsoft/TypeScript/issues/949
+            var castData = <Uint8Array> (<any> imageData.data);
+            castData.set(data);
+            context.putImageData(imageData, 0, 0);
+
+            var base64Image = screenshotCanvas.toDataURL();
+
+            //Creating a link if the browser have the download attribute on the a tag, to automatically start download generated image.
+            if (("download" in document.createElement("a"))) {
+                var a = window.document.createElement("a");
+                a.href = base64Image;
+                var date = new Date();
+                var stringDate = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate() + "-" + date.getHours() + ":" + date.getMinutes();
+                a.setAttribute("download", "screenshot-" + stringDate + ".png");
+
+                window.document.body.appendChild(a);
+
+                a.addEventListener("click",() => {
+                    a.parentElement.removeChild(a);
+                });
+                a.click();
+
+                //Or opening a new tab with the image if it is not possible to automatically start download.
+            } else {
+                var newWindow = window.open("");
+                var img = newWindow.document.createElement("img");
+                img.src = base64Image;
+                newWindow.document.body.appendChild(img);
+            }
+        }
+
         public static CreateScreenshot(engine: Engine, camera: Camera, size: any): void {
             var width: number;
             var height: number;
@@ -478,66 +544,7 @@
             texture.renderList = scene.meshes;
 
             texture.onAfterRender = () => {
-                // Read the contents of the framebuffer
-                var numberOfChannelsByLine = width * 4;
-                var halfHeight = height / 2;
-
-                //Reading datas from WebGL
-                var data = engine.readPixels(0, 0, width, height);
-
-                //To flip image on Y axis.
-                for (var i = 0; i < halfHeight; i++) {
-                    for (var j = 0; j < numberOfChannelsByLine; j++) {
-                        var currentCell = j + i * numberOfChannelsByLine;
-                        var targetLine = height - i - 1;
-                        var targetCell = j + targetLine * numberOfChannelsByLine;
-
-                        var temp = data[currentCell];
-                        data[currentCell] = data[targetCell];
-                        data[targetCell] = temp;
-                    }
-                }
-
-                // Create a 2D canvas to store the result
-                if (!screenshotCanvas) {
-                    screenshotCanvas = document.createElement('canvas');
-                }
-                screenshotCanvas.width = width;
-                screenshotCanvas.height = height;
-                var context = screenshotCanvas.getContext('2d');
-
-                // Copy the pixels to a 2D canvas
-                var imageData = context.createImageData(width, height);
-                //cast is due to ts error in lib.d.ts, see here - https://github.com/Microsoft/TypeScript/issues/949
-                var data = <Uint8Array> (<any> imageData.data);
-                data.set(data);
-                context.putImageData(imageData, 0, 0);
-
-                var base64Image = screenshotCanvas.toDataURL();
-
-                //Creating a link if the browser have the download attribute on the a tag, to automatically start download generated image.
-                if (("download" in document.createElement("a"))) {
-                    var a = window.document.createElement("a");
-                    a.href = base64Image;
-                    var date = new Date();
-                    var stringDate = date.getFullYear() + "/" + date.getMonth() + "/" + date.getDate() + "-" + date.getHours() + ":" + date.getMinutes();
-                    a.setAttribute("download", "screenshot-" + stringDate + ".png");
-
-                    window.document.body.appendChild(a);
-
-                    a.addEventListener("click",() => {
-                        a.parentElement.removeChild(a);
-                    });
-                    a.click();
-
-                    //Or opening a new tab with the image if it is not possible to automatically start download.
-                } else {
-                    var newWindow = window.open("");
-                    var img = newWindow.document.createElement("img");
-                    img.src = base64Image;
-                    newWindow.document.body.appendChild(img);
-                }
-
+                Tools.DumpFramebuffer(width, height, engine);
             };
 
             scene.incrementRenderId();

+ 69 - 7
Babylon/babylon.scene.js

@@ -91,6 +91,7 @@ var BABYLON;
             this.postProcessesEnabled = true;
             // Customs render targets
             this.renderTargetsEnabled = true;
+            this.dumpNextRenderTargets = false;
             this.customRenderTargets = new Array();
             // Imported meshes
             this.importedMeshesFiles = new Array();
@@ -100,6 +101,7 @@ var BABYLON;
             this.proceduralTexturesEnabled = true;
             this._proceduralTextures = new Array();
             this.soundTracks = new Array();
+            this._audioEnabled = true;
             this._totalVertices = 0;
             this._activeVertices = 0;
             this._activeParticles = 0;
@@ -917,7 +919,7 @@ var BABYLON;
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
                         this._renderId++;
-                        renderTarget.render();
+                        renderTarget.render(false, this.dumpNextRenderTargets);
                     }
                 }
                 BABYLON.Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
@@ -1076,7 +1078,7 @@ var BABYLON;
                         engine.setViewport(this.activeCamera.viewport);
                         // Camera
                         this.updateTransformMatrix();
-                        renderTarget.render();
+                        renderTarget.render(false, this.dumpNextRenderTargets);
                     }
                 }
                 BABYLON.Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
@@ -1146,11 +1148,14 @@ var BABYLON;
                 this._toBeDisposed[index] = null;
             }
             this._toBeDisposed.reset();
+            if (this.dumpNextRenderTargets) {
+                this.dumpNextRenderTargets = false;
+            }
             BABYLON.Tools.EndPerformanceCounter("Scene rendering");
             this._lastFrameDuration = BABYLON.Tools.Now - startDate;
         };
         Scene.prototype._updateAudioParameters = function () {
-            if (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 0) {
+            if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 0)) {
                 return;
             }
             var listeningCamera;
@@ -1183,6 +1188,46 @@ var BABYLON;
                 }
             }
         };
+        Object.defineProperty(Scene.prototype, "audioEnabled", {
+            get: function () {
+                return this._audioEnabled;
+            },
+            set: function (value) {
+                this._audioEnabled = value;
+                if (this._audioEnabled) {
+                    this._enableAudio();
+                }
+                else {
+                    this._disableAudio();
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Scene.prototype._disableAudio = function () {
+            for (var i = 0; i < this.mainSoundTrack.soundCollection.length; i++) {
+                this.mainSoundTrack.soundCollection[i].pause();
+            }
+            for (i = 0; i < this.soundTracks.length; i++) {
+                for (var j = 0; j < this.soundTracks[i].soundCollection.length; j++) {
+                    this.soundTracks[i].soundCollection[j].pause();
+                }
+            }
+        };
+        Scene.prototype._enableAudio = function () {
+            for (var i = 0; i < this.mainSoundTrack.soundCollection.length; i++) {
+                if (this.mainSoundTrack.soundCollection[i].isPaused) {
+                    this.mainSoundTrack.soundCollection[i].play();
+                }
+            }
+            for (i = 0; i < this.soundTracks.length; i++) {
+                for (var j = 0; j < this.soundTracks[i].soundCollection.length; j++) {
+                    if (this.soundTracks[i].soundCollection[j].isPaused) {
+                        this.soundTracks[i].soundCollection[j].play();
+                    }
+                }
+            }
+        };
         Scene.prototype.enableDepthRenderer = function () {
             if (this._depthRenderer) {
                 return this._depthRenderer;
@@ -1215,10 +1260,7 @@ var BABYLON;
             this._onAfterRenderCallbacks = [];
             this.detachControl();
             // Release sounds & sounds tracks
-            this.mainSoundTrack.dispose();
-            for (var scIndex = 0; scIndex < this.soundTracks.length; scIndex++) {
-                this.soundTracks[scIndex].dispose();
-            }
+            this.disposeSounds();
             // Detach cameras
             var canvas = this._engine.getRenderingCanvas();
             var index;
@@ -1262,6 +1304,13 @@ var BABYLON;
             }
             this._engine.wipeCaches();
         };
+        // Release sounds & sounds tracks
+        Scene.prototype.disposeSounds = function () {
+            this.mainSoundTrack.dispose();
+            for (var scIndex = 0; scIndex < this.soundTracks.length; scIndex++) {
+                this.soundTracks[scIndex].dispose();
+            }
+        };
         // Collisions
         Scene.prototype._getNewPosition = function (position, velocity, collider, maximumRetry, finalPosition, excludedMesh) {
             if (excludedMesh === void 0) { excludedMesh = null; }
@@ -1510,6 +1559,19 @@ var BABYLON;
         Scene.prototype.getMaterialByTags = function (tagsQuery, forEach) {
             return this._getByTags(this.materials, tagsQuery, forEach).concat(this._getByTags(this.multiMaterials, tagsQuery, forEach));
         };
+        // Audio
+        Scene.prototype.switchAudioModeForHeadphones = function () {
+            this.mainSoundTrack.switchPanningModelToHRTF();
+            for (var i = 0; i < this.soundTracks.length; i++) {
+                this.soundTracks[i].switchPanningModelToHRTF();
+            }
+        };
+        Scene.prototype.switchAudioModeForNormalSpeakers = function () {
+            this.mainSoundTrack.switchPanningModelToEqualPower();
+            for (var i = 0; i < this.soundTracks.length; i++) {
+                this.soundTracks[i].switchPanningModelToEqualPower();
+            }
+        };
         // Statics
         Scene._FOGMODE_NONE = 0;
         Scene._FOGMODE_EXP = 1;

+ 77 - 8
Babylon/babylon.scene.ts

@@ -162,6 +162,7 @@
 
         // Customs render targets
         public renderTargetsEnabled = true;
+        public dumpNextRenderTargets = false;
         public customRenderTargets = new Array<RenderTargetTexture>();
 
         // Delay loading
@@ -189,6 +190,7 @@
         // Sound Tracks
         public mainSoundTrack: SoundTrack;
         public soundTracks = new Array<SoundTrack>();
+        private _audioEnabled = true;
 
         // Private
         private _engine: Engine;
@@ -1186,10 +1188,12 @@
                     var renderTarget = this._renderTargets.data[renderIndex];
                     if (renderTarget._shouldRender()) {
                         this._renderId++;
-                        renderTarget.render();
+                        renderTarget.render(false, this.dumpNextRenderTargets);
                     }
                 }
                 Tools.EndPerformanceCounter("Render targets", this._renderTargets.length > 0);
+
+
                 this._renderId++;
             }
 
@@ -1381,7 +1385,7 @@
                         // Camera
                         this.updateTransformMatrix();
 
-                        renderTarget.render();
+                        renderTarget.render(false, this.dumpNextRenderTargets);
                     }
                 }
                 Tools.EndPerformanceCounter("Custom render targets", this.customRenderTargets.length > 0);
@@ -1468,13 +1472,16 @@
 
             this._toBeDisposed.reset();
 
+            if (this.dumpNextRenderTargets) {
+                this.dumpNextRenderTargets = false;
+            }
 
             Tools.EndPerformanceCounter("Scene rendering");
             this._lastFrameDuration = Tools.Now - startDate;
         }
 
         private _updateAudioParameters() {
-            if (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 0) {
+            if (!this.audioEnabled || (this.mainSoundTrack.soundCollection.length === 0 && this.soundTracks.length === 0)) {
                 return;
             }
 
@@ -1510,6 +1517,46 @@
             }
         }
 
+        public get audioEnabled(): boolean {
+            return this._audioEnabled;
+        }
+
+        public set audioEnabled(value: boolean) {
+            this._audioEnabled = value;
+            if (this._audioEnabled) {
+                this._enableAudio();
+                }
+            else {
+                this._disableAudio();
+            }
+        }
+
+        private _disableAudio() {
+            for (var i = 0; i < this.mainSoundTrack.soundCollection.length; i++) {
+                this.mainSoundTrack.soundCollection[i].pause();
+            }
+            for (i = 0; i < this.soundTracks.length; i++) {
+                for (var j = 0; j < this.soundTracks[i].soundCollection.length; j++) {
+                    this.soundTracks[i].soundCollection[j].pause();
+                }
+            }
+        }
+
+        private _enableAudio() {
+            for (var i = 0; i < this.mainSoundTrack.soundCollection.length; i++) {
+                if (this.mainSoundTrack.soundCollection[i].isPaused) {
+                    this.mainSoundTrack.soundCollection[i].play();
+                }
+            }
+            for (i = 0; i < this.soundTracks.length; i++) {
+                for (var j = 0; j < this.soundTracks[i].soundCollection.length; j++) {
+                    if (this.soundTracks[i].soundCollection[j].isPaused) {
+                        this.soundTracks[i].soundCollection[j].play();
+                    }
+                }
+            }
+        }
+
         public enableDepthRenderer(): DepthRenderer {
             if (this._depthRenderer) {
                 return this._depthRenderer;
@@ -1555,11 +1602,7 @@
             this.detachControl();
 
             // Release sounds & sounds tracks
-            this.mainSoundTrack.dispose();
-
-            for (var scIndex = 0; scIndex < this.soundTracks.length; scIndex++) {
-                this.soundTracks[scIndex].dispose();
-            }
+            this.disposeSounds();
 
             // Detach cameras
             var canvas = this._engine.getRenderingCanvas();
@@ -1626,6 +1669,15 @@
             this._engine.wipeCaches();
         }
 
+        // Release sounds & sounds tracks
+        public disposeSounds() {
+            this.mainSoundTrack.dispose();
+
+            for (var scIndex = 0; scIndex < this.soundTracks.length; scIndex++) {
+                this.soundTracks[scIndex].dispose();
+            }
+        }
+
         // Collisions
         public _getNewPosition(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, finalPosition: Vector3, excludedMesh: AbstractMesh = null): void {
             position.divideToRef(collider.radius, this._scaledPosition);
@@ -1935,5 +1987,22 @@
         public getMaterialByTags(tagsQuery: string, forEach?: (material: Material) => void): Material[] {
             return this._getByTags(this.materials, tagsQuery, forEach).concat(this._getByTags(this.multiMaterials, tagsQuery, forEach));
         }
+
+        // Audio
+        public switchAudioModeForHeadphones() {
+            this.mainSoundTrack.switchPanningModelToHRTF();
+
+            for (var i = 0; i < this.soundTracks.length; i++) {
+                this.soundTracks[i].switchPanningModelToHRTF();
+            }
+        }
+
+        public switchAudioModeForNormalSpeakers() {
+            this.mainSoundTrack.switchPanningModelToEqualPower();
+
+            for (var i = 0; i < this.soundTracks.length; i++) {
+                this.soundTracks[i].switchPanningModelToEqualPower();
+            }
+        }
     }
 } 

Разница между файлами не показана из-за своего большого размера
+ 401 - 182
babylon.2.1-alpha.debug.js


Разница между файлами не показана из-за своего большого размера
+ 19 - 19
babylon.2.1-alpha.js