فهرست منبع

Merge branch 'master' into resize-editor

Alejandro Toledo 5 سال پیش
والد
کامیت
ac53d45ec4
4فایلهای تغییر یافته به همراه156 افزوده شده و 110 حذف شده
  1. 1 0
      dist/preview release/what's new.md
  2. 2 0
      loaders/src/glTF/glTFFileLoader.ts
  3. 150 108
      src/Audio/sound.ts
  4. 3 2
      src/Misc/screenshotTools.ts

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

@@ -284,6 +284,7 @@
 - Fix docs Matrix.RotationYawPitchRoll and Matrix.RotationYawPitchRollToRef ([VSerain](https://github.com/VSerain))
 - Fix docs Matrix.RotationYawPitchRoll and Matrix.RotationYawPitchRollToRef ([VSerain](https://github.com/VSerain))
 - Fix issue in `GLTFLoader._updateBoneMatrices()` where bone rest position was not set. ([drigax](https://github.com/drigax))
 - Fix issue in `GLTFLoader._updateBoneMatrices()` where bone rest position was not set. ([drigax](https://github.com/drigax))
 - Fix the bounding box of instances that does not match the instance position / rotation / scaling ([Popov72](https://github.com/Popov72))
 - Fix the bounding box of instances that does not match the instance position / rotation / scaling ([Popov72](https://github.com/Popov72))
+- Fix an issue with sound updateOptions not updating the underlying sound buffer/html element ([RaananW](https://github.com/RaananW))
 
 
 ## Breaking changes
 ## Breaking changes
 
 

+ 2 - 0
loaders/src/glTF/glTFFileLoader.ts

@@ -251,6 +251,7 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
 
 
     /**
     /**
      * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
      * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+     * Note that the observable is raised as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
      */
      */
     public readonly onMeshLoadedObservable = new Observable<AbstractMesh>();
     public readonly onMeshLoadedObservable = new Observable<AbstractMesh>();
 
 
@@ -258,6 +259,7 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
 
 
     /**
     /**
      * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
      * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh.
+     * Note that the callback is called as soon as the mesh object is created, meaning some data may not have been setup yet for this mesh (vertex data, morph targets, material, ...)
      */
      */
     public set onMeshLoaded(callback: (mesh: AbstractMesh) => void) {
     public set onMeshLoaded(callback: (mesh: AbstractMesh) => void) {
         if (this._onMeshLoadedObserver) {
         if (this._onMeshLoadedObserver) {

+ 150 - 108
src/Audio/sound.ts

@@ -7,15 +7,15 @@ import { Engine } from "../Engines/engine";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { TransformNode } from "../Meshes/transformNode";
 import { TransformNode } from "../Meshes/transformNode";
 import { Logger } from "../Misc/logger";
 import { Logger } from "../Misc/logger";
-import { _DevTools } from '../Misc/devTools';
+import { _DevTools } from "../Misc/devTools";
 
 
 /**
 /**
  * Interface used to define options for Sound class
  * Interface used to define options for Sound class
  */
  */
 export interface ISoundOptions {
 export interface ISoundOptions {
     /**
     /**
-    * Does the sound autoplay once loaded.
-    */
+     * Does the sound autoplay once loaded.
+     */
     autoplay?: boolean;
     autoplay?: boolean;
     /**
     /**
      * Does the sound loop after it finishes playing once.
      * Does the sound loop after it finishes playing once.
@@ -38,9 +38,9 @@ export interface ISoundOptions {
      */
      */
     useCustomAttenuation?: boolean;
     useCustomAttenuation?: boolean;
     /**
     /**
-    * Define the roll off factor of spatial sounds.
-    * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
-    */
+     * Define the roll off factor of spatial sounds.
+     * @see https://doc.babylonjs.com/how_to/playing_sounds_and_music#creating-a-spatial-3d-sound
+     */
     rolloffFactor?: number;
     rolloffFactor?: number;
     /**
     /**
      * Define the reference distance the sound should be heard perfectly.
      * Define the reference distance the sound should be heard perfectly.
@@ -181,23 +181,23 @@ export class Sound {
     private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
     private _registerFunc: Nullable<(connectedMesh: TransformNode) => void>;
     private _isOutputConnected = false;
     private _isOutputConnected = false;
     private _htmlAudioElement: HTMLAudioElement;
     private _htmlAudioElement: HTMLAudioElement;
-    private _urlType: 'Unknown' | 'String' | 'Array' | 'ArrayBuffer' | 'MediaStream' = "Unknown";
+    private _urlType: "Unknown" | "String" | "Array" | "ArrayBuffer" | "MediaStream" = "Unknown";
     private _length?: number;
     private _length?: number;
     private _offset?: number;
     private _offset?: number;
 
 
     /** @hidden */
     /** @hidden */
     public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
     public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
         throw _DevTools.WarnImport("AudioSceneComponent");
         throw _DevTools.WarnImport("AudioSceneComponent");
-    }
+    };
 
 
     /**
     /**
-    * Create a sound and attach it to a scene
-    * @param name Name of your sound
-    * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
-    * @param scene defines the scene the sound belongs to
-    * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
-    * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
-    */
+     * Create a sound and attach it to a scene
+     * @param name Name of your sound
+     * @param urlOrArrayBuffer Url to the sound to load async or ArrayBuffer, it also works with MediaStreams
+     * @param scene defines the scene the sound belongs to
+     * @param readyToPlayCallback Provide a callback function if you'd like to load your code once the sound is ready to be played
+     * @param options Objects to provide with the current available options: autoplay, loop, volume, spatialSound, maxDistance, rolloffFactor, refDistance, distanceModel, panningModel, streaming
+     */
     constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback: Nullable<() => void> = null, options?: ISoundOptions) {
     constructor(name: string, urlOrArrayBuffer: any, scene: Scene, readyToPlayCallback: Nullable<() => void> = null, options?: ISoundOptions) {
         this.name = name;
         this.name = name;
         this._scene = scene;
         this._scene = scene;
@@ -208,8 +208,7 @@ export class Sound {
         this._customAttenuationFunction = (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => {
         this._customAttenuationFunction = (currentVolume: number, currentDistance: number, maxDistance: number, refDistance: number, rolloffFactor: number) => {
             if (currentDistance < maxDistance) {
             if (currentDistance < maxDistance) {
                 return currentVolume * (1 - currentDistance / maxDistance);
                 return currentVolume * (1 - currentDistance / maxDistance);
-            }
-            else {
+            } else {
                 return 0;
                 return 0;
             }
             }
         };
         };
@@ -246,7 +245,7 @@ export class Sound {
             // if no parameter is passed, you need to call setAudioBuffer yourself to prepare the sound
             // if no parameter is passed, you need to call setAudioBuffer yourself to prepare the sound
             if (urlOrArrayBuffer) {
             if (urlOrArrayBuffer) {
                 try {
                 try {
-                    if (typeof (urlOrArrayBuffer) === "string") {
+                    if (typeof urlOrArrayBuffer === "string") {
                         this._urlType = "String";
                         this._urlType = "String";
                     } else if (urlOrArrayBuffer instanceof ArrayBuffer) {
                     } else if (urlOrArrayBuffer instanceof ArrayBuffer) {
                         this._urlType = "ArrayBuffer";
                         this._urlType = "ArrayBuffer";
@@ -282,27 +281,37 @@ export class Sound {
                         case "String":
                         case "String":
                             urls.push(urlOrArrayBuffer);
                             urls.push(urlOrArrayBuffer);
                         case "Array":
                         case "Array":
-                            if (urls.length === 0) { urls = urlOrArrayBuffer; }
+                            if (urls.length === 0) {
+                                urls = urlOrArrayBuffer;
+                            }
                             // If we found a supported format, we load it immediately and stop the loop
                             // If we found a supported format, we load it immediately and stop the loop
                             for (var i = 0; i < urls.length; i++) {
                             for (var i = 0; i < urls.length; i++) {
                                 var url = urls[i];
                                 var url = urls[i];
-                                codecSupportedFound = (options && options.skipCodecCheck) ||
+                                codecSupportedFound =
+                                    (options && options.skipCodecCheck) ||
                                     (url.indexOf(".mp3", url.length - 4) !== -1 && Engine.audioEngine.isMP3supported) ||
                                     (url.indexOf(".mp3", url.length - 4) !== -1 && Engine.audioEngine.isMP3supported) ||
                                     (url.indexOf(".ogg", url.length - 4) !== -1 && Engine.audioEngine.isOGGsupported) ||
                                     (url.indexOf(".ogg", url.length - 4) !== -1 && Engine.audioEngine.isOGGsupported) ||
-                                    (url.indexOf(".wav", url.length - 4) !== -1) ||
-                                    (url.indexOf("blob:") !== -1);
+                                    url.indexOf(".wav", url.length - 4) !== -1 ||
+                                    url.indexOf("blob:") !== -1;
                                 if (codecSupportedFound) {
                                 if (codecSupportedFound) {
                                     // Loading sound using XHR2
                                     // Loading sound using XHR2
                                     if (!this._streaming) {
                                     if (!this._streaming) {
-                                        this._scene._loadFile(url, (data) => {
-                                            this._soundLoaded(data as ArrayBuffer);
-                                        }, undefined, true, true, (exception) => {
-                                            if (exception) {
-                                                Logger.Error("XHR " + exception.status + " error on: " + url + ".");
+                                        this._scene._loadFile(
+                                            url,
+                                            (data) => {
+                                                this._soundLoaded(data as ArrayBuffer);
+                                            },
+                                            undefined,
+                                            true,
+                                            true,
+                                            (exception) => {
+                                                if (exception) {
+                                                    Logger.Error("XHR " + exception.status + " error on: " + url + ".");
+                                                }
+                                                Logger.Error("Sound creation aborted.");
+                                                this._scene.mainSoundTrack.RemoveSound(this);
                                             }
                                             }
-                                            Logger.Error("Sound creation aborted.");
-                                            this._scene.mainSoundTrack.RemoveSound(this);
-                                        });
+                                        );
                                     }
                                     }
                                     // Streaming sound using HTML5 Audio tag
                                     // Streaming sound using HTML5 Audio tag
                                     else {
                                     else {
@@ -334,8 +343,7 @@ export class Sound {
 
 
                     if (!validParameter) {
                     if (!validParameter) {
                         Logger.Error("Parameter must be a URL to the sound, an Array of URLs (.mp3 & .ogg) or an ArrayBuffer of the sound.");
                         Logger.Error("Parameter must be a URL to the sound, an Array of URLs (.mp3 & .ogg) or an ArrayBuffer of the sound.");
-                    }
-                    else {
+                    } else {
                         if (!codecSupportedFound) {
                         if (!codecSupportedFound) {
                             this._isReadyToPlay = true;
                             this._isReadyToPlay = true;
                             // Simulating a ready to play event to avoid breaking code path
                             // Simulating a ready to play event to avoid breaking code path
@@ -348,14 +356,12 @@ export class Sound {
                             }
                             }
                         }
                         }
                     }
                     }
-                }
-                catch (ex) {
+                } catch (ex) {
                     Logger.Error("Unexpected error. Sound creation aborted.");
                     Logger.Error("Unexpected error. Sound creation aborted.");
                     this._scene.mainSoundTrack.RemoveSound(this);
                     this._scene.mainSoundTrack.RemoveSound(this);
                 }
                 }
             }
             }
-        }
-        else {
+        } else {
             // Adding an empty sound to avoid breaking audio calls for non Web Audio browsers
             // Adding an empty sound to avoid breaking audio calls for non Web Audio browsers
             this._scene.mainSoundTrack.AddSound(this);
             this._scene.mainSoundTrack.AddSound(this);
             if (!Engine.audioEngine.WarnedWebAudioUnsupported) {
             if (!Engine.audioEngine.WarnedWebAudioUnsupported) {
@@ -384,8 +390,7 @@ export class Sound {
             this._isReadyToPlay = false;
             this._isReadyToPlay = false;
             if (this.soundTrackId === -1) {
             if (this.soundTrackId === -1) {
                 this._scene.mainSoundTrack.RemoveSound(this);
                 this._scene.mainSoundTrack.RemoveSound(this);
-            }
-            else if (this._scene.soundTracks) {
+            } else if (this._scene.soundTracks) {
                 this._scene.soundTracks[this.soundTrackId].RemoveSound(this);
                 this._scene.soundTracks[this.soundTrackId].RemoveSound(this);
             }
             }
             if (this._soundGain) {
             if (this._soundGain) {
@@ -431,12 +436,22 @@ export class Sound {
         if (!Engine.audioEngine.audioContext) {
         if (!Engine.audioEngine.audioContext) {
             return;
             return;
         }
         }
-        Engine.audioEngine.audioContext.decodeAudioData(audioData, (buffer) => {
-            this._audioBuffer = buffer;
-            this._isReadyToPlay = true;
-            if (this.autoplay) { this.play(0, this._offset, this._length); }
-            if (this._readyToPlayCallback) { this._readyToPlayCallback(); }
-        }, (err: any) => { Logger.Error("Error while decoding audio data for: " + this.name + " / Error: " + err); });
+        Engine.audioEngine.audioContext.decodeAudioData(
+            audioData,
+            (buffer) => {
+                this._audioBuffer = buffer;
+                this._isReadyToPlay = true;
+                if (this.autoplay) {
+                    this.play(0, this._offset, this._length);
+                }
+                if (this._readyToPlayCallback) {
+                    this._readyToPlayCallback();
+                }
+            },
+            (err: any) => {
+                Logger.Error("Error while decoding audio data for: " + this.name + " / Error: " + err);
+            }
+        );
     }
     }
 
 
     /**
     /**
@@ -469,10 +484,21 @@ export class Sound {
             if (this.isPlaying) {
             if (this.isPlaying) {
                 if (this._streaming && this._htmlAudioElement) {
                 if (this._streaming && this._htmlAudioElement) {
                     this._htmlAudioElement.playbackRate = this._playbackRate;
                     this._htmlAudioElement.playbackRate = this._playbackRate;
-                }
-                else {
+                    if (this._htmlAudioElement.loop !== this.loop) {
+                        this._htmlAudioElement.loop = this.loop;
+                    }
+                } else {
                     if (this._soundSource) {
                     if (this._soundSource) {
                         this._soundSource.playbackRate.value = this._playbackRate;
                         this._soundSource.playbackRate.value = this._playbackRate;
+                        if (this._soundSource.loop !== this.loop) {
+                            this._soundSource.loop = this.loop;
+                        }
+                        if (this._offset !== undefined && this._soundSource.loopStart !== this._offset) {
+                            this._soundSource.loopStart = this._offset;
+                        }
+                        if (this._length !== undefined && this._length !== this._soundSource.loopEnd) {
+                            this._soundSource.loopEnd = (this._offset! | 0) + this._length!;
+                        }
                     }
                     }
                 }
                 }
             }
             }
@@ -500,8 +526,7 @@ export class Sound {
                 this._soundPanner.refDistance = 1;
                 this._soundPanner.refDistance = 1;
                 this._soundPanner.rolloffFactor = 1;
                 this._soundPanner.rolloffFactor = 1;
                 this._soundPanner.panningModel = this._panningModel as any;
                 this._soundPanner.panningModel = this._panningModel as any;
-            }
-            else {
+            } else {
                 this._soundPanner.distanceModel = this.distanceModel as any;
                 this._soundPanner.distanceModel = this.distanceModel as any;
                 this._soundPanner.maxDistance = this.maxDistance;
                 this._soundPanner.maxDistance = this.maxDistance;
                 this._soundPanner.refDistance = this.refDistance;
                 this._soundPanner.refDistance = this.refDistance;
@@ -552,11 +577,11 @@ export class Sound {
     }
     }
 
 
     /**
     /**
-    * Transform this sound into a directional source
-    * @param coneInnerAngle Size of the inner cone in degree
-    * @param coneOuterAngle Size of the outer cone in degree
-    * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
-    */
+     * Transform this sound into a directional source
+     * @param coneInnerAngle Size of the inner cone in degree
+     * @param coneOuterAngle Size of the outer cone in degree
+     * @param coneOuterGain Volume of the sound outside the outer cone (between 0.0 and 1.0)
+     */
     public setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void {
     public setDirectionalCone(coneInnerAngle: number, coneOuterAngle: number, coneOuterGain: number): void {
         if (coneOuterAngle < coneInnerAngle) {
         if (coneOuterAngle < coneInnerAngle) {
             Logger.Error("setDirectionalCone(): outer angle of the cone must be superior or equal to the inner angle.");
             Logger.Error("setDirectionalCone(): outer angle of the cone must be superior or equal to the inner angle.");
@@ -674,11 +699,11 @@ export class Sound {
     }
     }
 
 
     /**
     /**
-    * Play the sound
-    * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
-    * @param offset (optional) Start the sound at a specific time in seconds
-    * @param length (optional) Sound duration (in seconds)
-    */
+     * Play the sound
+     * @param time (optional) Start the sound after X seconds. Start immediately (0) by default.
+     * @param offset (optional) Start the sound at a specific time in seconds
+     * @param length (optional) Sound duration (in seconds)
+     */
     public play(time?: number, offset?: number, length?: number): void {
     public play(time?: number, offset?: number, length?: number): void {
         if (this._isReadyToPlay && this._scene.audioEnabled && Engine.audioEngine.audioContext) {
         if (this._isReadyToPlay && this._scene.audioEnabled && Engine.audioEngine.audioContext) {
             try {
             try {
@@ -698,8 +723,7 @@ export class Sound {
                             this._soundPanner.coneOuterGain = this._coneOuterGain;
                             this._soundPanner.coneOuterGain = this._coneOuterGain;
                             if (this._connectedTransformNode) {
                             if (this._connectedTransformNode) {
                                 this._updateDirection();
                                 this._updateDirection();
-                            }
-                            else {
+                            } else {
                                 this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
                                 this._soundPanner.setOrientation(this._localDirection.x, this._localDirection.y, this._localDirection.z);
                             }
                             }
                         }
                         }
@@ -708,7 +732,9 @@ export class Sound {
                 if (this._streaming) {
                 if (this._streaming) {
                     if (!this._streamingSource) {
                     if (!this._streamingSource) {
                         this._streamingSource = Engine.audioEngine.audioContext.createMediaElementSource(this._htmlAudioElement);
                         this._streamingSource = Engine.audioEngine.audioContext.createMediaElementSource(this._htmlAudioElement);
-                        this._htmlAudioElement.onended = () => { this._onended(); };
+                        this._htmlAudioElement.onended = () => {
+                            this._onended();
+                        };
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                         this._htmlAudioElement.playbackRate = this._playbackRate;
                     }
                     }
                     this._streamingSource.disconnect();
                     this._streamingSource.disconnect();
@@ -730,21 +756,23 @@ export class Sound {
                                         // Waiting for the audio engine to be unlocked by user click on unmute
                                         // Waiting for the audio engine to be unlocked by user click on unmute
                                         Engine.audioEngine.lock();
                                         Engine.audioEngine.lock();
                                         if (this.loop || this.autoplay) {
                                         if (this.loop || this.autoplay) {
-                                            Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                            Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
+                                                tryToPlay();
+                                            });
                                         }
                                         }
                                     });
                                     });
                                 }
                                 }
-                            }
-                            else {
+                            } else {
                                 if (this.loop || this.autoplay) {
                                 if (this.loop || this.autoplay) {
-                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
+                                        tryToPlay();
+                                    });
                                 }
                                 }
                             }
                             }
                         };
                         };
                         tryToPlay();
                         tryToPlay();
                     }
                     }
-                }
-                else {
+                } else {
                     var tryToPlay = () => {
                     var tryToPlay = () => {
                         if (Engine.audioEngine.audioContext) {
                         if (Engine.audioEngine.audioContext) {
                             length = length || this._length;
                             length = length || this._length;
@@ -767,7 +795,9 @@ export class Sound {
                                 this._soundSource.loopEnd = (offset! | 0) + length!;
                                 this._soundSource.loopEnd = (offset! | 0) + length!;
                             }
                             }
                             this._soundSource.playbackRate.value = this._playbackRate;
                             this._soundSource.playbackRate.value = this._playbackRate;
-                            this._soundSource.onended = () => { this._onended(); };
+                            this._soundSource.onended = () => {
+                                this._onended();
+                            };
                             startTime = time ? Engine.audioEngine.audioContext!.currentTime + time : Engine.audioEngine.audioContext!.currentTime;
                             startTime = time ? Engine.audioEngine.audioContext!.currentTime + time : Engine.audioEngine.audioContext!.currentTime;
                             const actualOffset = this.isPaused ? this._startOffset % this._soundSource!.buffer!.duration : offset ? offset : 0;
                             const actualOffset = this.isPaused ? this._startOffset % this._soundSource!.buffer!.duration : offset ? offset : 0;
                             this._soundSource!.start(startTime, actualOffset, this.loop ? undefined : length);
                             this._soundSource!.start(startTime, actualOffset, this.loop ? undefined : length);
@@ -782,23 +812,22 @@ export class Sound {
                                 // Waiting for the audio engine to be unlocked by user click on unmute
                                 // Waiting for the audio engine to be unlocked by user click on unmute
                                 Engine.audioEngine.lock();
                                 Engine.audioEngine.lock();
                                 if (this.loop || this.autoplay) {
                                 if (this.loop || this.autoplay) {
-                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => {
+                                        tryToPlay();
+                                    });
                                 }
                                 }
-                            }
-                            else {
+                            } else {
                                 tryToPlay();
                                 tryToPlay();
                             }
                             }
                         }, 500);
                         }, 500);
-                    }
-                    else {
+                    } else {
                         tryToPlay();
                         tryToPlay();
                     }
                     }
                 }
                 }
                 this._startTime = startTime;
                 this._startTime = startTime;
                 this.isPlaying = true;
                 this.isPlaying = true;
                 this.isPaused = false;
                 this.isPaused = false;
-            }
-            catch (ex) {
+            } catch (ex) {
                 Logger.Error("Error while trying to play audio: " + this.name + ", " + ex.message);
                 Logger.Error("Error while trying to play audio: " + this.name + ", " + ex.message);
             }
             }
         }
         }
@@ -813,9 +842,9 @@ export class Sound {
     }
     }
 
 
     /**
     /**
-    * Stop the sound
-    * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
-    */
+     * Stop the sound
+     * @param time (optional) Stop the sound after X seconds. Stop immediately (0) by default.
+     */
     public stop(time?: number): void {
     public stop(time?: number): void {
         if (this.isPlaying) {
         if (this.isPlaying) {
             if (this._streaming) {
             if (this._streaming) {
@@ -829,11 +858,12 @@ export class Sound {
                     this._streamingSource.disconnect();
                     this._streamingSource.disconnect();
                 }
                 }
                 this.isPlaying = false;
                 this.isPlaying = false;
-            }
-            else if (Engine.audioEngine.audioContext && this._soundSource) {
+            } else if (Engine.audioEngine.audioContext && this._soundSource) {
                 var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                 var stopTime = time ? Engine.audioEngine.audioContext.currentTime + time : Engine.audioEngine.audioContext.currentTime;
                 this._soundSource.stop(stopTime);
                 this._soundSource.stop(stopTime);
-                this._soundSource.onended = () => { this.isPlaying = false; };
+                this._soundSource.onended = () => {
+                    this.isPlaying = false;
+                };
                 if (!this.isPaused) {
                 if (!this.isPaused) {
                     this._startOffset = 0;
                     this._startOffset = 0;
                 }
                 }
@@ -853,8 +883,7 @@ export class Sound {
                 } else {
                 } else {
                     this._streamingSource.disconnect();
                     this._streamingSource.disconnect();
                 }
                 }
-            }
-            else if (Engine.audioEngine.audioContext) {
+            } else if (Engine.audioEngine.audioContext) {
                 this.stop(0);
                 this.stop(0);
                 this._startOffset += Engine.audioEngine.audioContext.currentTime - this._startTime;
                 this._startOffset += Engine.audioEngine.audioContext.currentTime - this._startTime;
             }
             }
@@ -872,8 +901,7 @@ export class Sound {
                 this._soundGain.gain.cancelScheduledValues(Engine.audioEngine.audioContext.currentTime);
                 this._soundGain.gain.cancelScheduledValues(Engine.audioEngine.audioContext.currentTime);
                 this._soundGain.gain.setValueAtTime(this._soundGain.gain.value, Engine.audioEngine.audioContext.currentTime);
                 this._soundGain.gain.setValueAtTime(this._soundGain.gain.value, Engine.audioEngine.audioContext.currentTime);
                 this._soundGain.gain.linearRampToValueAtTime(newVolume, Engine.audioEngine.audioContext.currentTime + time);
                 this._soundGain.gain.linearRampToValueAtTime(newVolume, Engine.audioEngine.audioContext.currentTime + time);
-            }
-            else {
+            } else {
                 this._soundGain.gain.value = newVolume;
                 this._soundGain.gain.value = newVolume;
             }
             }
         }
         }
@@ -889,8 +917,7 @@ export class Sound {
         if (this.isPlaying) {
         if (this.isPlaying) {
             if (this._streaming && this._htmlAudioElement) {
             if (this._streaming && this._htmlAudioElement) {
                 this._htmlAudioElement.playbackRate = this._playbackRate;
                 this._htmlAudioElement.playbackRate = this._playbackRate;
-            }
-            else if (this._soundSource) {
+            } else if (this._soundSource) {
                 this._soundSource.playbackRate.value = this._playbackRate;
                 this._soundSource.playbackRate.value = this._playbackRate;
             }
             }
         }
         }
@@ -944,8 +971,7 @@ export class Sound {
         if (this._positionInEmitterSpace) {
         if (this._positionInEmitterSpace) {
             node.worldMatrixFromCache.invertToRef(TmpVectors.Matrix[0]);
             node.worldMatrixFromCache.invertToRef(TmpVectors.Matrix[0]);
             this.setPosition(TmpVectors.Matrix[0].getTranslation());
             this.setPosition(TmpVectors.Matrix[0].getTranslation());
-        }
-        else {
+        } else {
             if (!(<any>node).getBoundingInfo) {
             if (!(<any>node).getBoundingInfo) {
                 this.setPosition(node.absolutePosition);
                 this.setPosition(node.absolutePosition);
             } else {
             } else {
@@ -969,18 +995,24 @@ export class Sound {
                 if (this._isReadyToPlay) {
                 if (this._isReadyToPlay) {
                     clonedSound._audioBuffer = this.getAudioBuffer();
                     clonedSound._audioBuffer = this.getAudioBuffer();
                     clonedSound._isReadyToPlay = true;
                     clonedSound._isReadyToPlay = true;
-                    if (clonedSound.autoplay) { clonedSound.play(0, this._offset, this._length); }
-                }
-                else {
+                    if (clonedSound.autoplay) {
+                        clonedSound.play(0, this._offset, this._length);
+                    }
+                } else {
                     window.setTimeout(setBufferAndRun, 300);
                     window.setTimeout(setBufferAndRun, 300);
                 }
                 }
             };
             };
 
 
             var currentOptions = {
             var currentOptions = {
-                autoplay: this.autoplay, loop: this.loop,
-                volume: this._volume, spatialSound: this.spatialSound, maxDistance: this.maxDistance,
-                useCustomAttenuation: this.useCustomAttenuation, rolloffFactor: this.rolloffFactor,
-                refDistance: this.refDistance, distanceModel: this.distanceModel
+                autoplay: this.autoplay,
+                loop: this.loop,
+                volume: this._volume,
+                spatialSound: this.spatialSound,
+                maxDistance: this.maxDistance,
+                useCustomAttenuation: this.useCustomAttenuation,
+                rolloffFactor: this.rolloffFactor,
+                refDistance: this.refDistance,
+                distanceModel: this.distanceModel,
             };
             };
 
 
             var clonedSound = new Sound(this.name + "_cloned", new ArrayBuffer(0), this._scene, null, currentOptions);
             var clonedSound = new Sound(this.name + "_cloned", new ArrayBuffer(0), this._scene, null, currentOptions);
@@ -1042,7 +1074,7 @@ export class Sound {
             playbackRate: this._playbackRate,
             playbackRate: this._playbackRate,
             panningModel: this._panningModel,
             panningModel: this._panningModel,
             soundTrackId: this.soundTrackId,
             soundTrackId: this.soundTrackId,
-            metadata: this.metadata
+            metadata: this.metadata,
         };
         };
 
 
         if (this.spatialSound) {
         if (this.spatialSound) {
@@ -1078,34 +1110,44 @@ export class Sound {
 
 
         if (parsedSound.url) {
         if (parsedSound.url) {
             soundUrl = rootUrl + parsedSound.url;
             soundUrl = rootUrl + parsedSound.url;
-        }
-        else {
+        } else {
             soundUrl = rootUrl + soundName;
             soundUrl = rootUrl + soundName;
         }
         }
 
 
         var options = {
         var options = {
-            autoplay: parsedSound.autoplay, loop: parsedSound.loop, volume: parsedSound.volume,
-            spatialSound: parsedSound.spatialSound, maxDistance: parsedSound.maxDistance,
+            autoplay: parsedSound.autoplay,
+            loop: parsedSound.loop,
+            volume: parsedSound.volume,
+            spatialSound: parsedSound.spatialSound,
+            maxDistance: parsedSound.maxDistance,
             rolloffFactor: parsedSound.rolloffFactor,
             rolloffFactor: parsedSound.rolloffFactor,
             refDistance: parsedSound.refDistance,
             refDistance: parsedSound.refDistance,
             distanceModel: parsedSound.distanceModel,
             distanceModel: parsedSound.distanceModel,
-            playbackRate: parsedSound.playbackRate
+            playbackRate: parsedSound.playbackRate,
         };
         };
 
 
         var newSound: Sound;
         var newSound: Sound;
 
 
         if (!sourceSound) {
         if (!sourceSound) {
-            newSound = new Sound(soundName, soundUrl, scene, () => { scene._removePendingData(newSound); }, options);
+            newSound = new Sound(
+                soundName,
+                soundUrl,
+                scene,
+                () => {
+                    scene._removePendingData(newSound);
+                },
+                options
+            );
             scene._addPendingData(newSound);
             scene._addPendingData(newSound);
-        }
-        else {
+        } else {
             var setBufferAndRun = () => {
             var setBufferAndRun = () => {
                 if (sourceSound._isReadyToPlay) {
                 if (sourceSound._isReadyToPlay) {
                     newSound._audioBuffer = sourceSound.getAudioBuffer();
                     newSound._audioBuffer = sourceSound.getAudioBuffer();
                     newSound._isReadyToPlay = true;
                     newSound._isReadyToPlay = true;
-                    if (newSound.autoplay) { newSound.play(0, newSound._offset, newSound._length); }
-                }
-                else {
+                    if (newSound.autoplay) {
+                        newSound.play(0, newSound._offset, newSound._length);
+                    }
+                } else {
                     window.setTimeout(setBufferAndRun, 300);
                     window.setTimeout(setBufferAndRun, 300);
                 }
                 }
             };
             };

+ 3 - 2
src/Misc/screenshotTools.ts

@@ -113,8 +113,9 @@ export class ScreenshotTools {
      * @param antialiasing Whether antialiasing should be turned on or not (default: false)
      * @param antialiasing Whether antialiasing should be turned on or not (default: false)
      * @param fileName A name for for the downloaded file.
      * @param fileName A name for for the downloaded file.
      * @param renderSprites Whether the sprites should be rendered or not (default: false)
      * @param renderSprites Whether the sprites should be rendered or not (default: false)
+     * @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
      */
      */
-    public static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType: string = "image/png", samples: number = 1, antialiasing: boolean = false, fileName?: string, renderSprites: boolean = false): void {
+    public static CreateScreenshotUsingRenderTarget(engine: Engine, camera: Camera, size: IScreenshotSize | number, successCallback?: (data: string) => void, mimeType: string = "image/png", samples: number = 1, antialiasing: boolean = false, fileName?: string, renderSprites: boolean = false, enableStencilBuffer: boolean = false): void {
         const { height, width } = ScreenshotTools._getScreenshotSize(engine, camera, size);
         const { height, width } = ScreenshotTools._getScreenshotSize(engine, camera, size);
         let targetTextureSize = { width, height };
         let targetTextureSize = { width, height };
 
 
@@ -142,7 +143,7 @@ export class ScreenshotTools {
         scene.render();
         scene.render();
 
 
         // At this point size can be a number, or an object (according to engine.prototype.createRenderTargetTexture method)
         // At this point size can be a number, or an object (according to engine.prototype.createRenderTargetTexture method)
-        var texture = new RenderTargetTexture("screenShot", targetTextureSize, scene, false, false, Constants.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE);
+        var texture = new RenderTargetTexture("screenShot", targetTextureSize, scene, false, false, Constants.TEXTURETYPE_UNSIGNED_INT, false, Texture.NEAREST_SAMPLINGMODE, undefined, enableStencilBuffer);
         texture.renderList = null;
         texture.renderList = null;
         texture.samples = samples;
         texture.samples = samples;
         texture.renderSprites = renderSprites;
         texture.renderSprites = renderSprites;