فهرست منبع

Merge branch 'master' into DirectedCylinderEmitter

David Catuhe 7 سال پیش
والد
کامیت
10d088941a
69فایلهای تغییر یافته به همراه11946 افزوده شده و 9639 حذف شده
  1. 2573 2314
      Playground/babylon.d.txt
  2. 2 1
      Tools/Gulp/config.json
  3. 5986 5693
      dist/preview release/babylon.d.ts
  4. 1 1
      dist/preview release/babylon.js
  5. 605 331
      dist/preview release/babylon.max.js
  6. 605 331
      dist/preview release/babylon.no-module.max.js
  7. 1 1
      dist/preview release/babylon.worker.js
  8. 607 333
      dist/preview release/es6.js
  9. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js
  10. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  11. 7 0
      dist/preview release/inspector/babylon.inspector.d.ts
  12. 16 0
      dist/preview release/inspector/babylon.inspector.module.d.ts
  13. 8 5
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  14. 9 6
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  15. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  16. 11 7
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  17. 14 9
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  19. 11 7
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  20. 14 9
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  22. 31 3
      dist/preview release/loaders/babylon.objFileLoader.d.ts
  23. 31 3
      dist/preview release/loaders/babylon.objFileLoader.js
  24. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  25. 42 10
      dist/preview release/loaders/babylonjs.loaders.d.ts
  26. 45 12
      dist/preview release/loaders/babylonjs.loaders.js
  27. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  28. 42 10
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  29. 1 15
      dist/preview release/viewer/babylon.viewer.d.ts
  30. 2 2
      dist/preview release/viewer/babylon.viewer.js
  31. 3 3
      dist/preview release/viewer/babylon.viewer.max.js
  32. 1 18
      dist/preview release/viewer/babylon.viewer.module.d.ts
  33. 8 3
      dist/preview release/what's new.md
  34. 208 80
      inspector/src/tabs/TextureTab.ts
  35. 1 1
      inspector/src/tabs/ToolsTab.ts
  36. BIN
      inspector/test/environment.dds
  37. 3 0
      inspector/test/index.js
  38. BIN
      inspector/test/test_1.dds
  39. 31 3
      loaders/src/OBJ/babylon.objFileLoader.ts
  40. 6 3
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  41. 11 8
      loaders/src/glTF/babylon.glTFFileLoader.ts
  42. 5 11
      src/Gizmos/babylon.boundingBoxGizmo.ts
  43. 4 0
      src/Instrumentation/babylon.sceneInstrumentation.ts
  44. 9 6
      src/Loading/babylon.sceneLoader.ts
  45. 9 55
      src/Mesh/babylon.abstractMesh.ts
  46. 0 14
      src/Mesh/babylon.linesMesh.ts
  47. 4 20
      src/Mesh/babylon.mesh.ts
  48. 23 2
      src/Particles/babylon.IParticleSystem.ts
  49. 11 1
      src/Particles/babylon.baseParticleSystem.ts
  50. 58 4
      src/Particles/babylon.gpuParticleSystem.ts
  51. 88 4
      src/Particles/babylon.particleSystem.ts
  52. 9 3
      src/Physics/Plugins/babylon.cannonJSPlugin.ts
  53. 19 17
      src/Physics/babylon.physicsJoint.ts
  54. 110 2
      src/Rendering/babylon.edgesRenderer.ts
  55. 166 8
      src/Rendering/babylon.outlineRenderer.ts
  56. 4 4
      src/Rendering/babylon.renderingGroup.ts
  57. 2 2
      src/Rendering/babylon.renderingManager.ts
  58. 2 1
      src/Shaders/gpuRenderParticles.fragment.fx
  59. 28 1
      src/Shaders/gpuUpdateParticles.vertex.fx
  60. 4 4
      src/Shaders/particles.fragment.fx
  61. 2 2
      src/Sprites/babylon.sprite.ts
  62. 46 1
      src/Sprites/babylon.spriteManager.ts
  63. 297 0
      src/Sprites/babylon.spriteSceneComponent.ts
  64. 61 244
      src/babylon.scene.ts
  65. 25 0
      src/babylon.sceneComponent.ts
  66. BIN
      tests/validation/ReferenceImages/edges.png
  67. BIN
      tests/validation/ReferenceImages/outline.png
  68. 14 4
      tests/validation/config.json
  69. 3 0
      tests/validation/validation.js

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2573 - 2314
Playground/babylon.d.txt


+ 2 - 1
Tools/Gulp/config.json

@@ -411,7 +411,8 @@
         "sprites": {
         "sprites": {
             "files": [
             "files": [
                 "../../src/Sprites/babylon.spriteManager.js",
                 "../../src/Sprites/babylon.spriteManager.js",
-                "../../src/Sprites/babylon.sprite.js"
+                "../../src/Sprites/babylon.sprite.js",
+                "../../src/Sprites/babylon.spriteSceneComponent.js"
             ],
             ],
             "dependUpon": [
             "dependUpon": [
                 "core"
                 "core"

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 5986 - 5693
dist/preview release/babylon.d.ts


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/babylon.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 605 - 331
dist/preview release/babylon.max.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 605 - 331
dist/preview release/babylon.no-module.max.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/babylon.worker.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 607 - 333
dist/preview release/es6.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js.map


+ 7 - 0
dist/preview release/inspector/babylon.inspector.d.ts

@@ -804,6 +804,7 @@ declare module INSPECTOR {
 }
 }
 declare module INSPECTOR {
 declare module INSPECTOR {
     export class TextureTab extends Tab {
     export class TextureTab extends Tab {
+        static DDSPreview: DDSPreview;
         /** The panel containing a list of items */
         /** The panel containing a list of items */
         protected _treePanel: HTMLElement;
         protected _treePanel: HTMLElement;
         protected _treeItems: Array<TreeItem>;
         protected _treeItems: Array<TreeItem>;
@@ -817,6 +818,12 @@ declare module INSPECTOR {
         /** Set the given item as active in the tree */
         /** Set the given item as active in the tree */
         activateNode(item: TreeItem): void;
         activateNode(item: TreeItem): void;
     }
     }
+    class DDSPreview {
+        canvas: HTMLCanvasElement | null;
+        constructor(AdapterItem: TextureAdapter);
+        insertPreview(AdapterItem: TextureAdapter): void;
+        dispose(): void;
+    }
 }
 }
 declare module INSPECTOR {
 declare module INSPECTOR {
     export class ToolsTab extends Tab {
     export class ToolsTab extends Tab {

+ 16 - 0
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -1040,11 +1040,13 @@ declare module 'babylonjs-inspector/tabs/TabBar' {
 }
 }
 
 
 declare module 'babylonjs-inspector/tabs/TextureTab' {
 declare module 'babylonjs-inspector/tabs/TextureTab' {
+    import { TextureAdapter } from "babylonjs-inspector/adapters/TextureAdapter";
     import { Inspector } from "babylonjs-inspector/Inspector";
     import { Inspector } from "babylonjs-inspector/Inspector";
     import { TreeItem } from "babylonjs-inspector/tree/TreeItem";
     import { TreeItem } from "babylonjs-inspector/tree/TreeItem";
     import { Tab } from "babylonjs-inspector/tabs/Tab";
     import { Tab } from "babylonjs-inspector/tabs/Tab";
     import { TabBar } from "babylonjs-inspector/tabs/TabBar";
     import { TabBar } from "babylonjs-inspector/tabs/TabBar";
     export class TextureTab extends Tab {
     export class TextureTab extends Tab {
+        static DDSPreview: DDSPreview;
         /** The panel containing a list of items */
         /** The panel containing a list of items */
         protected _treePanel: HTMLElement;
         protected _treePanel: HTMLElement;
         protected _treeItems: Array<TreeItem>;
         protected _treeItems: Array<TreeItem>;
@@ -1058,6 +1060,13 @@ declare module 'babylonjs-inspector/tabs/TextureTab' {
         /** Set the given item as active in the tree */
         /** Set the given item as active in the tree */
         activateNode(item: TreeItem): void;
         activateNode(item: TreeItem): void;
     }
     }
+    class DDSPreview {
+        canvas: HTMLCanvasElement | null;
+        constructor(AdapterItem: TextureAdapter);
+        insertPreview(AdapterItem: TextureAdapter): void;
+        dispose(): void;
+    }
+    export {};
 }
 }
 
 
 declare module 'babylonjs-inspector/tabs/ToolsTab' {
 declare module 'babylonjs-inspector/tabs/ToolsTab' {
@@ -2135,6 +2144,7 @@ declare module INSPECTOR {
 }
 }
 declare module INSPECTOR {
 declare module INSPECTOR {
     export class TextureTab extends Tab {
     export class TextureTab extends Tab {
+        static DDSPreview: DDSPreview;
         /** The panel containing a list of items */
         /** The panel containing a list of items */
         protected _treePanel: HTMLElement;
         protected _treePanel: HTMLElement;
         protected _treeItems: Array<TreeItem>;
         protected _treeItems: Array<TreeItem>;
@@ -2148,6 +2158,12 @@ declare module INSPECTOR {
         /** Set the given item as active in the tree */
         /** Set the given item as active in the tree */
         activateNode(item: TreeItem): void;
         activateNode(item: TreeItem): void;
     }
     }
+    class DDSPreview {
+        canvas: HTMLCanvasElement | null;
+        constructor(AdapterItem: TextureAdapter);
+        insertPreview(AdapterItem: TextureAdapter): void;
+        dispose(): void;
+    }
 }
 }
 declare module INSPECTOR {
 declare module INSPECTOR {
     export class ToolsTab extends Tab {
     export class ToolsTab extends Tab {

+ 8 - 5
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -76,13 +76,13 @@ declare module BABYLON {
     /** @hidden */
     /** @hidden */
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
     }
     /**
     /**
      * File loader for loading glTF files into a scene.
      * File loader for loading glTF files into a scene.
@@ -262,9 +262,10 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
@@ -276,18 +277,20 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         /**
         /**
          * Load into an asset container.
          * Load into an asset container.
          * @param scene The scene to load into
          * @param scene The scene to load into
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * If the data string can be loaded directly.
          * If the data string can be loaded directly.
          * @param data string contianing the file data
          * @param data string contianing the file data

+ 9 - 6
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -372,14 +372,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress);
+                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -388,14 +389,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress);
+                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -404,14 +406,15 @@ var BABYLON;
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(function (result) {
+                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fullName).then(function (result) {
                     var container = new BABYLON.AssetContainer(scene);
                     var container = new BABYLON.AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 11 - 7
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -76,13 +76,13 @@ declare module BABYLON {
     /** @hidden */
     /** @hidden */
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
     }
     /**
     /**
      * File loader for loading glTF files into a scene.
      * File loader for loading glTF files into a scene.
@@ -262,9 +262,10 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
@@ -276,18 +277,20 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         /**
         /**
          * Load into an asset container.
          * Load into an asset container.
          * @param scene The scene to load into
          * @param scene The scene to load into
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * If the data string can be loaded directly.
          * If the data string can be loaded directly.
          * @param data string contianing the file data
          * @param data string contianing the file data
@@ -568,6 +571,7 @@ declare module BABYLON.GLTF2 {
         private _state;
         private _state;
         private _extensions;
         private _extensions;
         private _rootUrl;
         private _rootUrl;
+        private _fullName;
         private _rootBabylonMesh;
         private _rootBabylonMesh;
         private _defaultBabylonMaterialData;
         private _defaultBabylonMaterialData;
         private _progressCallback?;
         private _progressCallback?;
@@ -596,14 +600,14 @@ declare module BABYLON.GLTF2 {
         /** @hidden */
         /** @hidden */
         dispose(): void;
         dispose(): void;
         /** @hidden */
         /** @hidden */
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
         /** @hidden */
         /** @hidden */
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         private _loadAsync;
         private _loadAsync;
         private _loadData;
         private _loadData;
         private _setupData;
         private _setupData;

+ 14 - 9
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -372,14 +372,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress);
+                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -388,14 +389,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress);
+                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -404,14 +406,15 @@ var BABYLON;
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(function (result) {
+                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fullName).then(function (result) {
                     var container = new BABYLON.AssetContainer(scene);
                     var container = new BABYLON.AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
@@ -812,11 +815,12 @@ var BABYLON;
                 this._parent._clear();
                 this._parent._clear();
             };
             };
             /** @hidden */
             /** @hidden */
-            GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+            GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
                 var _this = this;
                 var _this = this;
                 return Promise.resolve().then(function () {
                 return Promise.resolve().then(function () {
                     _this.babylonScene = scene;
                     _this.babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._rootUrl = rootUrl;
+                    _this._fullName = fullName || "" + Date.now();
                     _this._progressCallback = onProgress;
                     _this._progressCallback = onProgress;
                     _this._loadData(data);
                     _this._loadData(data);
                     var nodes = null;
                     var nodes = null;
@@ -850,11 +854,12 @@ var BABYLON;
                 });
                 });
             };
             };
             /** @hidden */
             /** @hidden */
-            GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+            GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
                 var _this = this;
                 var _this = this;
                 return Promise.resolve().then(function () {
                 return Promise.resolve().then(function () {
                     _this.babylonScene = scene;
                     _this.babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._rootUrl = rootUrl;
+                    _this._fullName = fullName || "" + Date.now();
                     _this._progressCallback = onProgress;
                     _this._progressCallback = onProgress;
                     _this._loadData(data);
                     _this._loadData(data);
                     return _this._loadAsync(null, function () { return undefined; });
                     return _this._loadAsync(null, function () { return undefined; });
@@ -2106,7 +2111,7 @@ var BABYLON;
                 babylonTexture.wrapV = samplerData.wrapV;
                 babylonTexture.wrapV = samplerData.wrapV;
                 var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
                 var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
                 promises.push(this.loadImageAsync("#/images/" + image.index, image).then(function (data) {
                 promises.push(this.loadImageAsync("#/images/" + image.index, image).then(function (data) {
-                    var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image.index);
+                    var dataUrl = "data:" + _this._fullName + (image.uri || "image" + image.index);
                     babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                     babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 }));
                 assign(babylonTexture);
                 assign(babylonTexture);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 11 - 7
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -76,13 +76,13 @@ declare module BABYLON {
     /** @hidden */
     /** @hidden */
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
     }
     /**
     /**
      * File loader for loading glTF files into a scene.
      * File loader for loading glTF files into a scene.
@@ -262,9 +262,10 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
@@ -276,18 +277,20 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         /**
         /**
          * Load into an asset container.
          * Load into an asset container.
          * @param scene The scene to load into
          * @param scene The scene to load into
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * If the data string can be loaded directly.
          * If the data string can be loaded directly.
          * @param data string contianing the file data
          * @param data string contianing the file data
@@ -1130,6 +1133,7 @@ declare module BABYLON.GLTF2 {
         private _state;
         private _state;
         private _extensions;
         private _extensions;
         private _rootUrl;
         private _rootUrl;
+        private _fullName;
         private _rootBabylonMesh;
         private _rootBabylonMesh;
         private _defaultBabylonMaterialData;
         private _defaultBabylonMaterialData;
         private _progressCallback?;
         private _progressCallback?;
@@ -1158,14 +1162,14 @@ declare module BABYLON.GLTF2 {
         /** @hidden */
         /** @hidden */
         dispose(): void;
         dispose(): void;
         /** @hidden */
         /** @hidden */
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
         /** @hidden */
         /** @hidden */
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         private _loadAsync;
         private _loadAsync;
         private _loadData;
         private _loadData;
         private _setupData;
         private _setupData;

+ 14 - 9
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -372,14 +372,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress);
+                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -388,14 +389,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress);
+                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -404,14 +406,15 @@ var BABYLON;
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(function (result) {
+                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fullName).then(function (result) {
                     var container = new BABYLON.AssetContainer(scene);
                     var container = new BABYLON.AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
@@ -3019,11 +3022,12 @@ var BABYLON;
                 this._parent._clear();
                 this._parent._clear();
             };
             };
             /** @hidden */
             /** @hidden */
-            GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+            GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
                 var _this = this;
                 var _this = this;
                 return Promise.resolve().then(function () {
                 return Promise.resolve().then(function () {
                     _this.babylonScene = scene;
                     _this.babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._rootUrl = rootUrl;
+                    _this._fullName = fullName || "" + Date.now();
                     _this._progressCallback = onProgress;
                     _this._progressCallback = onProgress;
                     _this._loadData(data);
                     _this._loadData(data);
                     var nodes = null;
                     var nodes = null;
@@ -3057,11 +3061,12 @@ var BABYLON;
                 });
                 });
             };
             };
             /** @hidden */
             /** @hidden */
-            GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+            GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
                 var _this = this;
                 var _this = this;
                 return Promise.resolve().then(function () {
                 return Promise.resolve().then(function () {
                     _this.babylonScene = scene;
                     _this.babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._rootUrl = rootUrl;
+                    _this._fullName = fullName || "" + Date.now();
                     _this._progressCallback = onProgress;
                     _this._progressCallback = onProgress;
                     _this._loadData(data);
                     _this._loadData(data);
                     return _this._loadAsync(null, function () { return undefined; });
                     return _this._loadAsync(null, function () { return undefined; });
@@ -4313,7 +4318,7 @@ var BABYLON;
                 babylonTexture.wrapV = samplerData.wrapV;
                 babylonTexture.wrapV = samplerData.wrapV;
                 var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
                 var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
                 promises.push(this.loadImageAsync("#/images/" + image.index, image).then(function (data) {
                 promises.push(this.loadImageAsync("#/images/" + image.index, image).then(function (data) {
-                    var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image.index);
+                    var dataUrl = "data:" + _this._fullName + (image.uri || "image" + image.index);
                     babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                     babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 }));
                 assign(babylonTexture);
                 assign(babylonTexture);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 31 - 3
dist/preview release/loaders/babylon.objFileLoader.d.ts

@@ -58,14 +58,42 @@ declare module BABYLON {
          * @private
          * @private
          */
          */
         private _loadMTL;
         private _loadMTL;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
-        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
          * Each mesh contains all information given by the OBJ and the MTL file.

+ 31 - 3
dist/preview release/loaders/babylon.objFileLoader.js

@@ -251,7 +251,17 @@ var BABYLON;
             // Loads through the babylon tools to allow fileInput search.
             // Loads through the babylon tools to allow fileInput search.
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
         };
         };
-        OBJFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        OBJFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
             //get the meshes from OBJ file
             //get the meshes from OBJ file
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(function (meshes) {
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(function (meshes) {
                 return {
                 return {
@@ -262,13 +272,31 @@ var BABYLON;
                 };
                 };
             });
             });
         };
         };
-        OBJFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        OBJFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
             //Get the 3D model
             //Get the 3D model
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(function () {
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(function () {
                 // return void
                 // return void
             });
             });
         };
         };
-        OBJFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        OBJFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fullName) {
             return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
             return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
                 var container = new BABYLON.AssetContainer(scene);
                 var container = new BABYLON.AssetContainer(scene);
                 result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
                 result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 42 - 10
dist/preview release/loaders/babylonjs.loaders.d.ts

@@ -76,14 +76,42 @@ declare module BABYLON {
          * @private
          * @private
          */
          */
         private _loadMTL;
         private _loadMTL;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
-        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
          * Each mesh contains all information given by the OBJ and the MTL file.
@@ -178,13 +206,13 @@ declare module BABYLON {
     /** @hidden */
     /** @hidden */
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
     }
     /**
     /**
      * File loader for loading glTF files into a scene.
      * File loader for loading glTF files into a scene.
@@ -364,9 +392,10 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
@@ -378,18 +407,20 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         /**
         /**
          * Load into an asset container.
          * Load into an asset container.
          * @param scene The scene to load into
          * @param scene The scene to load into
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * If the data string can be loaded directly.
          * If the data string can be loaded directly.
          * @param data string contianing the file data
          * @param data string contianing the file data
@@ -1232,6 +1263,7 @@ declare module BABYLON.GLTF2 {
         private _state;
         private _state;
         private _extensions;
         private _extensions;
         private _rootUrl;
         private _rootUrl;
+        private _fullName;
         private _rootBabylonMesh;
         private _rootBabylonMesh;
         private _defaultBabylonMaterialData;
         private _defaultBabylonMaterialData;
         private _progressCallback?;
         private _progressCallback?;
@@ -1260,14 +1292,14 @@ declare module BABYLON.GLTF2 {
         /** @hidden */
         /** @hidden */
         dispose(): void;
         dispose(): void;
         /** @hidden */
         /** @hidden */
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
         /** @hidden */
         /** @hidden */
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         private _loadAsync;
         private _loadAsync;
         private _loadData;
         private _loadData;
         private _setupData;
         private _setupData;

+ 45 - 12
dist/preview release/loaders/babylonjs.loaders.js

@@ -450,7 +450,17 @@ var BABYLON;
             // Loads through the babylon tools to allow fileInput search.
             // Loads through the babylon tools to allow fileInput search.
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
             BABYLON.Tools.LoadFile(pathOfFile, onSuccess, undefined, undefined, false, function () { console.warn("Error - Unable to load " + pathOfFile); });
         };
         };
-        OBJFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        OBJFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
             //get the meshes from OBJ file
             //get the meshes from OBJ file
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(function (meshes) {
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(function (meshes) {
                 return {
                 return {
@@ -461,13 +471,31 @@ var BABYLON;
                 };
                 };
             });
             });
         };
         };
-        OBJFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        OBJFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
             //Get the 3D model
             //Get the 3D model
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(function () {
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(function () {
                 // return void
                 // return void
             });
             });
         };
         };
-        OBJFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        OBJFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fullName) {
             return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
             return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
                 var container = new BABYLON.AssetContainer(scene);
                 var container = new BABYLON.AssetContainer(scene);
                 result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
                 result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
@@ -1427,14 +1455,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress);
+                return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -1443,14 +1472,15 @@ var BABYLON;
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress);
+                return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         };
         };
         /**
         /**
@@ -1459,14 +1489,15 @@ var BABYLON;
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress) {
+        GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fullName) {
             var _this = this;
             var _this = this;
             return Promise.resolve().then(function () {
             return Promise.resolve().then(function () {
                 var loaderData = _this._parse(data);
                 var loaderData = _this._parse(data);
                 _this._loader = _this._getLoader(loaderData);
                 _this._loader = _this._getLoader(loaderData);
-                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(function (result) {
+                return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fullName).then(function (result) {
                     var container = new BABYLON.AssetContainer(scene);
                     var container = new BABYLON.AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
@@ -4050,11 +4081,12 @@ var BABYLON;
                 this._parent._clear();
                 this._parent._clear();
             };
             };
             /** @hidden */
             /** @hidden */
-            GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress) {
+            GLTFLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fullName) {
                 var _this = this;
                 var _this = this;
                 return Promise.resolve().then(function () {
                 return Promise.resolve().then(function () {
                     _this.babylonScene = scene;
                     _this.babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._rootUrl = rootUrl;
+                    _this._fullName = fullName || "" + Date.now();
                     _this._progressCallback = onProgress;
                     _this._progressCallback = onProgress;
                     _this._loadData(data);
                     _this._loadData(data);
                     var nodes = null;
                     var nodes = null;
@@ -4088,11 +4120,12 @@ var BABYLON;
                 });
                 });
             };
             };
             /** @hidden */
             /** @hidden */
-            GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress) {
+            GLTFLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fullName) {
                 var _this = this;
                 var _this = this;
                 return Promise.resolve().then(function () {
                 return Promise.resolve().then(function () {
                     _this.babylonScene = scene;
                     _this.babylonScene = scene;
                     _this._rootUrl = rootUrl;
                     _this._rootUrl = rootUrl;
+                    _this._fullName = fullName || "" + Date.now();
                     _this._progressCallback = onProgress;
                     _this._progressCallback = onProgress;
                     _this._loadData(data);
                     _this._loadData(data);
                     return _this._loadAsync(null, function () { return undefined; });
                     return _this._loadAsync(null, function () { return undefined; });
@@ -5344,7 +5377,7 @@ var BABYLON;
                 babylonTexture.wrapV = samplerData.wrapV;
                 babylonTexture.wrapV = samplerData.wrapV;
                 var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
                 var image = ArrayItem.Get(context + "/source", this.gltf.images, texture.source);
                 promises.push(this.loadImageAsync("#/images/" + image.index, image).then(function (data) {
                 promises.push(this.loadImageAsync("#/images/" + image.index, image).then(function (data) {
-                    var dataUrl = "data:" + _this._rootUrl + (image.uri || "image" + image.index);
+                    var dataUrl = "data:" + _this._fullName + (image.uri || "image" + image.index);
                     babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                     babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 }));
                 }));
                 assign(babylonTexture);
                 assign(babylonTexture);

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.min.js


+ 42 - 10
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -83,14 +83,42 @@ declare module BABYLON {
          * @private
          * @private
          */
          */
         private _loadMTL;
         private _loadMTL;
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
-        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * Read the OBJ file and create an Array of meshes.
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
          * Each mesh contains all information given by the OBJ and the MTL file.
@@ -185,13 +213,13 @@ declare module BABYLON {
     /** @hidden */
     /** @hidden */
     interface IGLTFLoader extends IDisposable {
     interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
     }
     /**
     /**
      * File loader for loading glTF files into a scene.
      * File loader for loading glTF files into a scene.
@@ -371,9 +399,10 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
@@ -385,18 +414,20 @@ declare module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         /**
         /**
          * Load into an asset container.
          * Load into an asset container.
          * @param scene The scene to load into
          * @param scene The scene to load into
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
         /**
         /**
          * If the data string can be loaded directly.
          * If the data string can be loaded directly.
          * @param data string contianing the file data
          * @param data string contianing the file data
@@ -1239,6 +1270,7 @@ declare module BABYLON.GLTF2 {
         private _state;
         private _state;
         private _extensions;
         private _extensions;
         private _rootUrl;
         private _rootUrl;
+        private _fullName;
         private _rootBabylonMesh;
         private _rootBabylonMesh;
         private _defaultBabylonMaterialData;
         private _defaultBabylonMaterialData;
         private _progressCallback?;
         private _progressCallback?;
@@ -1267,14 +1299,14 @@ declare module BABYLON.GLTF2 {
         /** @hidden */
         /** @hidden */
         dispose(): void;
         dispose(): void;
         /** @hidden */
         /** @hidden */
-        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{
+        importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{
             meshes: AbstractMesh[];
             meshes: AbstractMesh[];
             particleSystems: IParticleSystem[];
             particleSystems: IParticleSystem[];
             skeletons: Skeleton[];
             skeletons: Skeleton[];
             animationGroups: AnimationGroup[];
             animationGroups: AnimationGroup[];
         }>;
         }>;
         /** @hidden */
         /** @hidden */
-        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
         private _loadAsync;
         private _loadAsync;
         private _loadData;
         private _loadData;
         private _setupData;
         private _setupData;

+ 1 - 15
dist/preview release/viewer/babylon.viewer.d.ts

@@ -924,7 +924,7 @@ declare module BabylonViewer {
       * @param name the name of the custom optimizer configuration
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
@@ -1558,20 +1558,6 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-declare module BabylonViewer {
 }
 }
 declare module BabylonViewer {
 declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {
     export interface IEnvironmentMapConfiguration {

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 2 - 2
dist/preview release/viewer/babylon.viewer.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 3 - 3
dist/preview release/viewer/babylon.viewer.max.js


+ 1 - 18
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -985,14 +985,13 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 }
 
 
 declare module 'babylonjs-viewer/optimizer/custom' {
 declare module 'babylonjs-viewer/optimizer/custom' {
-    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     /**
     /**
       *
       *
       * @param name the name of the custom optimizer configuration
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 }
 
 
@@ -1663,22 +1662,6 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 }
 
 
-declare module 'babylonjs-viewer/optimizer/custom/extended' {
-    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
-    /**
-        * A custom upgrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedUpgrade(sceneManager: SceneManager): boolean;
-    /**
-        * A custom degrade-oriented function configuration for the scene optimizer.
-        *
-        * @param viewer the viewer to optimize
-        */
-    export function extendedDegrade(sceneManager: SceneManager): boolean;
-}
-
 declare module 'babylonjs-viewer/configuration/interfaces' {
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';

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

@@ -15,6 +15,7 @@
   - Added ignoreChildren field to bounding box to save performance when using heavily nested meshes ([TrevorDev](https://github.com/TrevorDev))
   - Added ignoreChildren field to bounding box to save performance when using heavily nested meshes ([TrevorDev](https://github.com/TrevorDev))
   - Add uniform scaling drag support to scale gizmo ([TrevorDev](https://github.com/TrevorDev))
   - Add uniform scaling drag support to scale gizmo ([TrevorDev](https://github.com/TrevorDev))
   - Support interacting with child elements ([TrevorDev](https://github.com/TrevorDev))
   - Support interacting with child elements ([TrevorDev](https://github.com/TrevorDev))
+  - BoundingBox gizmo support for including/excluding descendants when computing the bounding box ([TrevorDev](https://github.com/TrevorDev))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
 - Particle system improvements ([Deltakosh](https://github.com/deltakosh))
   - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
   - Added a ParticleHelper class to create some pre-configured particle systems in a one-liner method style. [Doc](https://doc.babylonjs.com/How_To/ParticleHelper) ([Deltakosh](https://github.com/deltakosh)) / ([DevChris](https://github.com/yovanoc))
   - Improved CPU particles rendering performance (up to x2 on low end devices)
   - Improved CPU particles rendering performance (up to x2 on low end devices)
@@ -34,8 +35,11 @@
   - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
   - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
   - Added support for velocity gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#velocity-over-time)
   - Added support for velocity gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#velocity-over-time)
   - Added support for limit velocity gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time)
   - Added support for limit velocity gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#limit-velocity-over-time)
+  - Added support for drag gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#drag-factor)
   - Added support for noise textures. [Doc](http://doc.babylonjs.com/babylon101/particles#noise-texture)
   - Added support for noise textures. [Doc](http://doc.babylonjs.com/babylon101/particles#noise-texture)
   - Added support for emit rate gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#emit-rate-over-time)
   - Added support for emit rate gradients. [Doc](http://doc.babylonjs.com/babylon101/particles#emit-rate-over-time)
+  - Start size gradient support for particles. [Doc](http://doc.babylonjs.com/babylon101/particles#start-size-over-time) ([TrevorDev](https://github.com/TrevorDev))
+  - Cylinder particle emitter and constructor in baseParticle [Doc](https://doc.babylonjs.com/babylon101/particles#cylinder-emitter) ([TrevorDev](https://github.com/TrevorDev))
 - Added SceneComponent to help decoupling Scene from its components. ([sebavan](http://www.github.com/sebavan))
 - Added SceneComponent to help decoupling Scene from its components. ([sebavan](http://www.github.com/sebavan))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - Added [Environment Texture Tools](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture) to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan))
 - Playground can now be used with TypeScript directly!. [Demo](https://www.babylonjs-playground.com/ts.html) ([Deltakosh](https://github.com/deltakosh), [NasimiAsl](https://github.com/NasimiAsl))
 - Playground can now be used with TypeScript directly!. [Demo](https://www.babylonjs-playground.com/ts.html) ([Deltakosh](https://github.com/deltakosh), [NasimiAsl](https://github.com/NasimiAsl))
@@ -47,7 +51,7 @@
 ## Updates
 ## Updates
 
 
 - Updated TypeScript version to new major 3.0.1 ([christopherstock](https://github.com/christopherstock))
 - Updated TypeScript version to new major 3.0.1 ([christopherstock](https://github.com/christopherstock))
-- All NPM packages have `latest`and `preview` streams [#3055](https://github.com/BabylonJS/Babylon.js/issues/3055) ([RaananW](https://github.com/RaananW))
+- All NPM packages have `latest` and `preview` streams [#3055](https://github.com/BabylonJS/Babylon.js/issues/3055) ([RaananW](https://github.com/RaananW))
 - Added New Tools Tab in the inspector (env texture and screenshot tools so far) ([sebavan](http://www.github.com/sebavan))
 - Added New Tools Tab in the inspector (env texture and screenshot tools so far) ([sebavan](http://www.github.com/sebavan))
 - Moved to gulp 4, updated dependencies to latest ([RaananW](https://github.com/RaananW))
 - Moved to gulp 4, updated dependencies to latest ([RaananW](https://github.com/RaananW))
 
 
@@ -60,7 +64,7 @@
 ### Core Engine
 ### Core Engine
 
 
 - Added `scene.pickSpriteWithRay` function ([Deltakosh](https://github.com/deltakosh))
 - Added `scene.pickSpriteWithRay` function ([Deltakosh](https://github.com/deltakosh))
-- Added support for muyltiple clip planes. [Demo](https://www.babylonjs-playground.com/#Y6W087) ([Deltakosh](https://github.com/deltakosh))
+- Added support for multiple clip planes. [Demo](https://www.babylonjs-playground.com/#Y6W087) ([Deltakosh](https://github.com/deltakosh))
 - Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
 - Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
 - Added new `BoundingInfo.scale()` function to let users control the size of the bounding info ([Deltakosh](https://github.com/deltakosh))
 - Added new `BoundingInfo.scale()` function to let users control the size of the bounding info ([Deltakosh](https://github.com/deltakosh))
 - Added new `Animatable.waitAsync` function to use Promises with animations. Demo [Here](https://www.babylonjs-playground.com/#HZBCXR) ([Deltakosh](https://github.com/deltakosh))
 - Added new `Animatable.waitAsync` function to use Promises with animations. Demo [Here](https://www.babylonjs-playground.com/#HZBCXR) ([Deltakosh](https://github.com/deltakosh))
@@ -109,7 +113,6 @@
 - Added EdgesLineRenderer to address [#4919](https://github.com/BabylonJS/Babylon.js/pull/4919) ([barteq100](https://github.com/barteq100))
 - Added EdgesLineRenderer to address [#4919](https://github.com/BabylonJS/Babylon.js/pull/4919) ([barteq100](https://github.com/barteq100))
 - Added ```ambientTextureImpactOnAnalyticalLights``` in PBRMaterial to allow fine grained control of the AmbientTexture on the analytical diffuse light ([sebavan](http://www.github.com/sebavan))
 - Added ```ambientTextureImpactOnAnalyticalLights``` in PBRMaterial to allow fine grained control of the AmbientTexture on the analytical diffuse light ([sebavan](http://www.github.com/sebavan))
 - BoundingBoxGizmo scalePivot field that can be used to always scale objects from the bottom ([TrevorDev](https://github.com/TrevorDev))
 - BoundingBoxGizmo scalePivot field that can be used to always scale objects from the bottom ([TrevorDev](https://github.com/TrevorDev))
-- Cylinder particle emitter and constructor in baseParticle ([TrevorDev](https://github.com/TrevorDev))
 - Improved _isSyncronized performance and reduced GC in TransformNode.computeWorldMatrix by directly reading property. ([Bolloxim](https://github.com/Bolloxim))
 - Improved _isSyncronized performance and reduced GC in TransformNode.computeWorldMatrix by directly reading property. ([Bolloxim](https://github.com/Bolloxim))
 - Added supports for reflectionMatrix in Skybox Mode Cube Texture allowing offsetting the world center or rotating the matrix ([sebavan](http://www.github.com/sebavan))
 - Added supports for reflectionMatrix in Skybox Mode Cube Texture allowing offsetting the world center or rotating the matrix ([sebavan](http://www.github.com/sebavan))
 
 
@@ -178,6 +181,7 @@
 - Do not modify pivot point when using bounding box gizmo or behaviors ([TrevorDev](https://github.com/TrevorDev))
 - Do not modify pivot point when using bounding box gizmo or behaviors ([TrevorDev](https://github.com/TrevorDev))
 - GPUParticleSystem does not get stuck in burst loop when stopped and started ([TrevorDev](https://github.com/TrevorDev))
 - GPUParticleSystem does not get stuck in burst loop when stopped and started ([TrevorDev](https://github.com/TrevorDev))
 - trackPosition:false not working in webVRCamera ([TrevorDev](https://github.com/TrevorDev))
 - trackPosition:false not working in webVRCamera ([TrevorDev](https://github.com/TrevorDev))
+- Spring Joint could not be removed ([TrevorDev](https://github.com/TrevorDev))
 
 
 ### Core Engine
 ### Core Engine
 
 
@@ -219,6 +223,7 @@
 
 
 - STL Loader only supported binary downloads and no data: urls [#4473](https://github.com/BabylonJS/Babylon.js/issues/4473) ([RaananW](https://github.com/RaananW))
 - STL Loader only supported binary downloads and no data: urls [#4473](https://github.com/BabylonJS/Babylon.js/issues/4473) ([RaananW](https://github.com/RaananW))
 - OBJ Loader is now an async loader [#4571](https://github.com/BabylonJS/Babylon.js/issues/4571) ([RaananW](https://github.com/RaananW))
 - OBJ Loader is now an async loader [#4571](https://github.com/BabylonJS/Babylon.js/issues/4571) ([RaananW](https://github.com/RaananW))
+- GLTF Loader does not have texture conflicts on subsequent loads anymore [#5030](https://github.com/BabylonJS/Babylon.js/issues/5030) ([sebavan](http://www.github.com/sebavan))
 
 
 ## Breaking changes
 ## Breaking changes
 
 

+ 208 - 80
inspector/src/tabs/TextureTab.ts

@@ -1,4 +1,4 @@
-import { CubeTexture, RenderTargetTexture, Tools } from "babylonjs";
+import { CubeTexture, RenderTargetTexture, Tools, Vector3 } from "babylonjs";
 import { TextureAdapter } from "../adapters/TextureAdapter";
 import { TextureAdapter } from "../adapters/TextureAdapter";
 import { Helpers } from "../helpers/Helpers";
 import { Helpers } from "../helpers/Helpers";
 import { Inspector } from "../Inspector";
 import { Inspector } from "../Inspector";
@@ -10,6 +10,8 @@ import * as Split from "Split";
 
 
 export class TextureTab extends Tab {
 export class TextureTab extends Tab {
 
 
+    static DDSPreview: DDSPreview;
+
     private _inspector: Inspector;
     private _inspector: Inspector;
     /** The panel containing a list of items */
     /** The panel containing a list of items */
     protected _treePanel: HTMLElement;
     protected _treePanel: HTMLElement;
@@ -39,7 +41,7 @@ export class TextureTab extends Tab {
     }
     }
 
 
     public dispose() {
     public dispose() {
-        // Nothing to dispose
+        TextureTab.DDSPreview.dispose();
     }
     }
 
 
     public update(_items?: Array<TreeItem>) {
     public update(_items?: Array<TreeItem>) {
@@ -85,95 +87,127 @@ export class TextureTab extends Tab {
         Helpers.CleanDiv(this._imagePanel);
         Helpers.CleanDiv(this._imagePanel);
         // Get the texture object
         // Get the texture object
         let texture = item.adapter.object;
         let texture = item.adapter.object;
-        let imgs: HTMLImageElement[] = [];
-        let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
-        imgs.push(img);
-        //Create five other images elements
-        for (let i = 0; i < 5; i++) {
-            imgs.push(Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement);
-        }
-
-        if (texture instanceof RenderTargetTexture) {
-            // RenderTarget textures
-            let scene = this._inspector.scene;
-            let engine = scene.getEngine();
-            let size = texture.getSize();
-
-            // Clone the texture
-            let screenShotTexture = texture.clone();
-            screenShotTexture.activeCamera = texture.activeCamera;
-            screenShotTexture.onBeforeRender = texture.onBeforeRender;
-            screenShotTexture.onAfterRender = texture.onAfterRender;
-            screenShotTexture.onBeforeRenderObservable = texture.onBeforeRenderObservable;
-
-            // To display the texture after rendering
-            screenShotTexture.onAfterRenderObservable.add((faceIndex: number) => {
-                Tools.DumpFramebuffer(size.width, size.height, engine,
-                    (data) => imgs[faceIndex].src = data);
-            });
-
-            // Render the texture
-            scene.incrementRenderId();
-            scene.resetCachedMaterial();
-            screenShotTexture.render();
-            screenShotTexture.dispose();
-        } else if (texture instanceof CubeTexture) {
-            // Cannot open correctly DDS File
-            // Display all textures of the CubeTexture
-            let pixels = <ArrayBufferView>texture.readPixels();
-            let canvas = document.createElement('canvas');
-            canvas.id = "MyCanvas";
-
-            if (img.parentElement) {
-                img.parentElement.appendChild(canvas);
-            }
-            let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
-            let size = texture.getSize();
+        let imageExtension = item.adapter.object.name.split('.').pop();
+        //In case the texture is a standard image format
+        if (imageExtension == "png" || imageExtension == "jpg" || imageExtension == "gif" || imageExtension == "svg") {
 
 
-            let tmp = pixels.buffer.slice(0, size.height * size.width * 4);
-            let u = new Uint8ClampedArray(tmp)
+            let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
 
 
-            let colors = new ImageData(size.width * 6, size.height);
+            img.style.width = this._imagePanel.style.width;
 
 
-            colors.data.set(u);
-            let imgData = ctx.createImageData(size.width * 6, size.height);
+            img.style.height = "auto";
 
 
-            imgData.data.set(u);
+            img.src = (<BABYLON.Texture>texture).name;
 
 
-            // let data = imgData.data;
+        } else if (imageExtension == "dds") {
+            //In case the texture is a dds format
 
 
-            // for(let i = 0, len = size.height * size.width; i < len; i++) {
-            //     data[i] = pixels[i];
-            // }
-            ctx.putImageData(imgData, 0, 0);
-            // let i: number = 0;
-            // for(let filename of (texture as CubeTexture)['_files']){
-            //     imgs[i].src = filename;
-            //     i++;
-            // }
+            if (TextureTab.DDSPreview != null && TextureTab.DDSPreview.canvas != null) {
+                this._imagePanel.appendChild(<Node>TextureTab.DDSPreview.canvas);
+                TextureTab.DDSPreview.insertPreview(item.adapter);
+            }
+            else {
+                //Create a canvas to load BJS if it don't exists
+                let previewCanvas = Helpers.CreateElement('canvas', '', this._imagePanel);
+                previewCanvas.style.outline = "none";
+                previewCanvas.style.webkitTapHighlightColor = "rgba(255,255,255,0)";
+                previewCanvas.id = "babylonjs-inspector-textures-preview";
+
+                TextureTab.DDSPreview = new DDSPreview(item.adapter);
+            }
         }
         }
-        else if (texture['_canvas']) {
-            // Dynamic texture
-            let base64Image = texture['_canvas'].toDataURL("image/png");
-            img.src = base64Image;
-        } else if (texture.url) {
-            let pixels = texture.readPixels();
-            let canvas = document.createElement('canvas');
-            canvas.id = "MyCanvas";
-
-            if (img.parentElement) {
-                img.parentElement.appendChild(canvas);
+        else {
+            let imgs: HTMLImageElement[] = [];
+            let img = Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement;
+            imgs.push(img);
+            //Create five other images elements
+            for (let i = 0; i < 5; i++) {
+                imgs.push(Helpers.CreateElement('img', 'texture-image', this._imagePanel) as HTMLImageElement);
+            }
+
+            if (texture instanceof RenderTargetTexture) {
+                // RenderTarget textures
+                let scene = this._inspector.scene;
+                let engine = scene.getEngine();
+                let size = texture.getSize();
+
+                // Clone the texture
+                let screenShotTexture = texture.clone();
+                screenShotTexture.activeCamera = texture.activeCamera;
+                screenShotTexture.onBeforeRender = texture.onBeforeRender;
+                screenShotTexture.onAfterRender = texture.onAfterRender;
+                screenShotTexture.onBeforeRenderObservable = texture.onBeforeRenderObservable;
+
+                // To display the texture after rendering
+                screenShotTexture.onAfterRenderObservable.add((faceIndex: number) => {
+                    Tools.DumpFramebuffer(size.width, size.height, engine,
+                        (data) => imgs[faceIndex].src = data);
+                });
+
+                // Render the texture
+                scene.incrementRenderId();
+                scene.resetCachedMaterial();
+                screenShotTexture.render();
+                screenShotTexture.dispose();
+            } else if (texture instanceof CubeTexture) {
+                // Cannot open correctly DDS File
+                // Display all textures of the CubeTexture
+                let pixels = <ArrayBufferView>texture.readPixels();
+                let canvas = document.createElement('canvas');
+                canvas.id = "MyCanvas";
+
+                if (img.parentElement) {
+                    img.parentElement.appendChild(canvas);
+                }
+                let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
+                let size = texture.getSize();
+
+                let tmp = pixels.buffer.slice(0, size.height * size.width * 4);
+                let u = new Uint8ClampedArray(tmp)
+
+                let colors = new ImageData(size.width * 6, size.height);
+
+                colors.data.set(u);
+                let imgData = ctx.createImageData(size.width * 6, size.height);
+
+                imgData.data.set(u);
+
+                // let data = imgData.data;
+
+                // for(let i = 0, len = size.height * size.width; i < len; i++) {
+                //     data[i] = pixels[i];
+                // }
+                ctx.putImageData(imgData, 0, 0);
+                // let i: number = 0;
+                // for(let filename of (texture as CubeTexture)['_files']){
+                //     imgs[i].src = filename;
+                //     i++;
+                // }
             }
             }
-            let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
-            let size = texture.getSize();
+            else if (texture['_canvas']) {
+                // Dynamic texture
+                let base64Image = texture['_canvas'].toDataURL("image/png");
+                img.src = base64Image;
+            } else if (texture.url) {
+                let pixels = texture.readPixels();
+                let canvas = document.createElement('canvas');
+                canvas.id = "MyCanvas";
+
+                if (img.parentElement) {
+                    img.parentElement.appendChild(canvas);
+                }
+                let ctx = <CanvasRenderingContext2D>canvas.getContext('2d');
+                let size = texture.getSize();
+
+                let imgData = ctx.createImageData(size.width, size.height);
 
 
-            let imgData = ctx.createImageData(size.width, size.height);
+                imgData.data.set(pixels);
 
 
-            imgData.data.set(pixels);
+                ctx.putImageData(imgData, 0, 0);
+                // If an url is present, the texture is an image
+                // img.src = texture.url;
 
 
-            ctx.putImageData(imgData, 0, 0);
-            // If an url is present, the texture is an image
-            // img.src = texture.url;
+            }
 
 
         }
         }
 
 
@@ -196,4 +230,98 @@ export class TextureTab extends Tab {
         }
         }
         item.active(true);
         item.active(true);
     }
     }
+
+}
+
+class DDSPreview {
+
+    public canvas: HTMLCanvasElement | null;
+    private _engine: BABYLON.Engine;
+    private _scene: BABYLON.Scene;
+    private _camera: BABYLON.ArcRotateCamera;
+    private _mat: BABYLON.StandardMaterial;
+    private _tex: BABYLON.Texture;
+    private _cubeTex: BABYLON.CubeTexture;
+    private _mesh: BABYLON.Mesh;
+
+    constructor(AdapterItem: TextureAdapter) {
+
+        this.canvas = document.getElementById("babylonjs-inspector-textures-preview") as HTMLCanvasElement;
+        this._engine = new BABYLON.Engine(this.canvas, true);
+
+        this._run();
+        this.insertPreview(AdapterItem);
+    }
+
+    private _run() {
+        this._scene = new BABYLON.Scene(this._engine);
+        this._scene.clearColor = new BABYLON.Color4(0.1412, 0.1412, 0.1412, 1);
+
+        let light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), this._scene);
+        light.intensity = 1;
+
+        this._camera = new BABYLON.ArcRotateCamera("Camera", 0, 1.57, 5, Vector3.Zero(), this._scene);
+        this._scene.activeCamera = this._camera;
+        this._camera.attachControl(this.canvas as HTMLCanvasElement);
+
+        window.addEventListener("resize", () => {
+            this._engine.resize();
+        });
+
+        this._scene.executeWhenReady(() => {
+            this._engine.runRenderLoop(() => {
+                this._scene.render();
+            });
+        });
+    }
+
+    public insertPreview(AdapterItem: TextureAdapter) {
+        if (this._tex) this._tex.dispose();
+        if (this._mat) this._mat.dispose();
+        if (this._mesh) this._mesh.dispose();
+
+        this._mat = new BABYLON.StandardMaterial("customMat", this._scene);
+
+        if (AdapterItem.type() == "Texture") {
+            //If the dds is not a cube format render it on a plane
+
+            var previewMeshPlane = BABYLON.Mesh.CreatePlane("previewPlane", 3, this._scene);
+            previewMeshPlane.rotate(new BABYLON.Vector3(1, 0, 0), 3.14);
+            previewMeshPlane.rotate(new BABYLON.Vector3(0, 1, 0), -1.57);
+            this._mesh = previewMeshPlane;
+
+            this._tex = new BABYLON.Texture(AdapterItem.object.name, this._scene);
+            this._tex.invertZ = true;
+            this._tex.uScale = -1;
+
+            this._mat.diffuseTexture = this._tex;
+            this._mat.emissiveTexture = this._tex;
+            this._mat.specularTexture = this._tex;
+            this._mat.disableLighting = true;
+
+            previewMeshPlane.material = this._mat;
+
+        }
+        else if (AdapterItem.type() == "BaseTexture") {
+            //Else if the dds is a cube format render it on a box
+
+            var previewMeshBox = BABYLON.Mesh.CreateBox("previewBox", 3, this._scene);
+            previewMeshBox.rotate(new BABYLON.Vector3(0, 1, 0), -0.5);
+            this._mesh = previewMeshBox;
+
+            this._cubeTex = new BABYLON.CubeTexture(AdapterItem.object.name, this._scene);
+            this._mat.reflectionTexture = this._cubeTex;
+            (<BABYLON.CubeTexture>this._mat.reflectionTexture).coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
+            this._mat.disableLighting = true;
+
+            previewMeshBox.material = this._mat;
+        }
+
+        this._engine.resize();
+    }
+
+    public dispose() {
+        this._engine.dispose();
+        this.canvas = null;
+    }
 }
 }

+ 1 - 1
inspector/src/tabs/ToolsTab.ts

@@ -141,7 +141,7 @@ export class ToolsTab extends Tab {
             };
             };
             elemValue.appendChild(inputElement);
             elemValue.appendChild(inputElement);
 
 
-            if (VideoRecorder.IsSupported(this._scene.getEngine())) {
+            if (VideoRecorder && VideoRecorder.IsSupported(this._scene.getEngine())) {
                 let videoRecorderElement = Inspector.DOCUMENT.createElement('input');
                 let videoRecorderElement = Inspector.DOCUMENT.createElement('input');
                 videoRecorderElement.value = "Start Recording Video";
                 videoRecorderElement.value = "Start Recording Video";
                 videoRecorderElement.type = "button";
                 videoRecorderElement.type = "button";

BIN
inspector/test/environment.dds


+ 3 - 0
inspector/test/index.js

@@ -56,6 +56,9 @@ var Test = (function () {
 
 
         var tn = new BABYLON.TransformNode("transform node");
         var tn = new BABYLON.TransformNode("transform node");
 
 
+        let DDSTexture = new BABYLON.CubeTexture("test/environment.dds", scene);
+        let DDSTexture2 = new BABYLON.Texture("test/test_1.dds", scene);
+
         // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
         // Our built-in 'ground' shape. Params: name, width, depth, subdivs, scene
         var ground = BABYLON.Mesh.CreateGround("node_damagedHelmet_-6514", 6, 6, 2, scene);
         var ground = BABYLON.Mesh.CreateGround("node_damagedHelmet_-6514", 6, 6, 2, scene);
         ground.parent = tn;
         ground.parent = tn;

BIN
inspector/test/test_1.dds


+ 31 - 3
loaders/src/OBJ/babylon.objFileLoader.ts

@@ -254,7 +254,17 @@ module BABYLON {
                 () => { console.warn("Error - Unable to load " + pathOfFile); });
                 () => { console.warn("Error - Unable to load " + pathOfFile); });
         }
         }
 
 
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
+        /**
+         * Imports one or more meshes from the loaded glTF data and adds them to the scene
+         * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
+         * @param scene the scene the meshes should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise containg the loaded meshes, particles, skeletons and animations
+         */
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             //get the meshes from OBJ file
             //get the meshes from OBJ file
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(meshes => {
             return this._parseSolid(meshesNames, scene, data, rootUrl).then(meshes => {
                 return {
                 return {
@@ -266,14 +276,32 @@ module BABYLON {
             });
             });
         }
         }
 
 
-        public loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
+        /**
+         * Imports all objects from the loaded glTF data and adds them to the scene
+         * @param scene the scene the objects should be added to
+         * @param data the glTF data to load
+         * @param rootUrl root url to load from
+         * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
+         * @returns a promise which completes when objects have been loaded to the scene
+         */
+        public loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void> {
             //Get the 3D model
             //Get the 3D model
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(() => {
             return this.importMeshAsync(null, scene, data, rootUrl, onProgress).then(() => {
                 // return void
                 // return void
             });
             });
         }
         }
 
 
-        public loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer> {
+        /**
+         * Load into an asset container.
+         * @param scene The scene to load into
+         * @param data The data to import
+         * @param rootUrl The root url for scene and resources
+         * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
+         * @returns The loaded asset container
+         */
+        public loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer> {
             return this.importMeshAsync(null, scene, data, rootUrl).then(result => {
             return this.importMeshAsync(null, scene, data, rootUrl).then(result => {
                 var container = new AssetContainer(scene);
                 var container = new AssetContainer(scene);
                 result.meshes.forEach(mesh => container.meshes.push(mesh));
                 result.meshes.forEach(mesh => container.meshes.push(mesh));

+ 6 - 3
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -64,6 +64,7 @@ module BABYLON.GLTF2 {
         private _state: Nullable<GLTFLoaderState> = null;
         private _state: Nullable<GLTFLoaderState> = null;
         private _extensions: { [name: string]: IGLTFLoaderExtension } = {};
         private _extensions: { [name: string]: IGLTFLoaderExtension } = {};
         private _rootUrl: string;
         private _rootUrl: string;
+        private _fullName: string;
         private _rootBabylonMesh: Mesh;
         private _rootBabylonMesh: Mesh;
         private _defaultBabylonMaterialData: { [drawMode: number]: Material } = {};
         private _defaultBabylonMaterialData: { [drawMode: number]: Material } = {};
         private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
         private _progressCallback?: (event: SceneLoaderProgressEvent) => void;
@@ -156,10 +157,11 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         /** @hidden */
         /** @hidden */
-        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
             return Promise.resolve().then(() => {
                 this.babylonScene = scene;
                 this.babylonScene = scene;
                 this._rootUrl = rootUrl;
                 this._rootUrl = rootUrl;
+                this._fullName = fullName || `${Date.now()}`;
                 this._progressCallback = onProgress;
                 this._progressCallback = onProgress;
                 this._loadData(data);
                 this._loadData(data);
 
 
@@ -198,10 +200,11 @@ module BABYLON.GLTF2 {
         }
         }
 
 
         /** @hidden */
         /** @hidden */
-        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
+        public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void> {
             return Promise.resolve().then(() => {
             return Promise.resolve().then(() => {
                 this.babylonScene = scene;
                 this.babylonScene = scene;
                 this._rootUrl = rootUrl;
                 this._rootUrl = rootUrl;
+                this._fullName = fullName || `${Date.now()}`;
                 this._progressCallback = onProgress;
                 this._progressCallback = onProgress;
                 this._loadData(data);
                 this._loadData(data);
                 return this._loadAsync(null, () => undefined);
                 return this._loadAsync(null, () => undefined);
@@ -1648,7 +1651,7 @@ module BABYLON.GLTF2 {
 
 
             const image = ArrayItem.Get(`${context}/source`, this.gltf.images, texture.source);
             const image = ArrayItem.Get(`${context}/source`, this.gltf.images, texture.source);
             promises.push(this.loadImageAsync(`#/images/${image.index}`, image).then(data => {
             promises.push(this.loadImageAsync(`#/images/${image.index}`, image).then(data => {
-                const dataUrl = `data:${this._rootUrl}${image.uri || `image${image.index}`}`;
+                const dataUrl = `data:${this._fullName}${image.uri || `image${image.index}`}`;
                 babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
                 babylonTexture.updateURL(dataUrl, new Blob([data], { type: image.mimeType }));
             }));
             }));
 
 

+ 11 - 8
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -89,8 +89,8 @@ module BABYLON {
     /** @hidden */
     /** @hidden */
     export interface IGLTFLoader extends IDisposable {
     export interface IGLTFLoader extends IDisposable {
         readonly state: Nullable<GLTFLoaderState>;
         readonly state: Nullable<GLTFLoaderState>;
-        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
-        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void) => Promise<void>;
+        importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
+        loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string) => Promise<void>;
     }
     }
 
 
     /**
     /**
@@ -445,13 +445,14 @@ module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          * @returns a promise containg the loaded meshes, particles, skeletons and animations
          */
          */
-        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
+        public importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
             return Promise.resolve().then(() => {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
-                return this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress);
+                return this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         }
         }
 
 
@@ -461,13 +462,14 @@ module BABYLON {
          * @param data the glTF data to load
          * @param data the glTF data to load
          * @param rootUrl root url to load from
          * @param rootUrl root url to load from
          * @param onProgress event that fires when loading progress has occured
          * @param onProgress event that fires when loading progress has occured
+         * @param fullName Defines the FQDN of the file to load
          * @returns a promise which completes when objects have been loaded to the scene
          * @returns a promise which completes when objects have been loaded to the scene
          */
          */
-        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
+        public loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void> {
             return Promise.resolve().then(() => {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
-                return this._loader.loadAsync(scene, loaderData, rootUrl, onProgress);
+                return this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fullName);
             });
             });
         }
         }
 
 
@@ -477,13 +479,14 @@ module BABYLON {
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        public loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer> {
+        public loadAssetContainerAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer> {
             return Promise.resolve().then(() => {
             return Promise.resolve().then(() => {
                 const loaderData = this._parse(data);
                 const loaderData = this._parse(data);
                 this._loader = this._getLoader(loaderData);
                 this._loader = this._getLoader(loaderData);
-                return this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress).then(result => {
+                return this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fullName).then(result => {
                     const container = new AssetContainer(scene);
                     const container = new AssetContainer(scene);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.meshes, result.meshes);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                     Array.prototype.push.apply(container.particleSystems, result.particleSystems);

+ 5 - 11
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -18,6 +18,10 @@ module BABYLON {
          * If child meshes should be ignored when calculating the boudning box. This should be set to true to avoid perf hits with heavily nested meshes (Default: false)
          * If child meshes should be ignored when calculating the boudning box. This should be set to true to avoid perf hits with heavily nested meshes (Default: false)
          */
          */
         public ignoreChildren = false;
         public ignoreChildren = false;
+        /**
+         * Returns true if a descendant should be included when computing the bounding box. When null, all descendants are included. If ignoreChildren is set this will be ignored. (Default: null)
+         */
+        public includeChildPredicate: Nullable<(abstractMesh: AbstractMesh) => boolean> = null
 
 
         /**
         /**
          * The size of the rotation spheres attached to the bounding box (Default: 0.1)
          * The size of the rotation spheres attached to the bounding box (Default: 0.1)
@@ -332,15 +336,6 @@ module BABYLON {
                 })
                 })
         }
         }
 
 
-        private _recurseComputeWorld(node: Node) {
-            node.computeWorldMatrix(true);
-            if(!this.ignoreChildren){
-                node.getDescendants().forEach((n) => {
-                    this._recurseComputeWorld(n);
-                });
-            }
-        }
-
         /**
         /**
          * Updates the bounding box information for the Gizmo
          * Updates the bounding box information for the Gizmo
          */
          */
@@ -364,7 +359,7 @@ module BABYLON {
                 this.attachedMesh.position.set(0, 0, 0);
                 this.attachedMesh.position.set(0, 0, 0);
 
 
                 // Update bounding dimensions/positions   
                 // Update bounding dimensions/positions   
-                var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors(!this.ignoreChildren);
+                var boundingMinMax = this.attachedMesh.getHierarchyBoundingVectors(!this.ignoreChildren, this.includeChildPredicate);
                 boundingMinMax.max.subtractToRef(boundingMinMax.min, this._boundingDimensions);
                 boundingMinMax.max.subtractToRef(boundingMinMax.min, this._boundingDimensions);
 
 
                 // Update gizmo to match bounding box scaling and rotation
                 // Update gizmo to match bounding box scaling and rotation
@@ -378,7 +373,6 @@ module BABYLON {
                 // restore position/rotation values
                 // restore position/rotation values
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.rotationQuaternion.copyFrom(this._tmpQuaternion);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
                 this.attachedMesh.position.copyFrom(this._tmpVector);
-                this._recurseComputeWorld(this.attachedMesh);
             }
             }
             
             
             // Update rotation sphere locations
             // Update rotation sphere locations

+ 4 - 0
src/Instrumentation/babylon.sceneInstrumentation.ts

@@ -214,6 +214,10 @@ module BABYLON {
 
 
             this._captureSpritesRenderTime = value;
             this._captureSpritesRenderTime = value;
 
 
+            if (!this.scene.spriteManagers) {
+                return;
+            }
+
             if (value) {
             if (value) {
                 this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(() => {
                 this._onBeforeSpritesRenderingObserver = this.scene.onBeforeSpritesRenderingObservable.add(() => {
                     Tools.StartPerformanceCounter("Sprites");
                     Tools.StartPerformanceCounter("Sprites");

+ 9 - 6
src/Loading/babylon.sceneLoader.ts

@@ -94,9 +94,10 @@
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded meshes, particle systems, skeletons, and animation groups
          * @returns The loaded meshes, particle systems, skeletons, and animation groups
          */
          */
-        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
+        importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<{ meshes: AbstractMesh[], particleSystems: IParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }>;
 
 
         /**
         /**
          * Load into a scene.
          * Load into a scene.
@@ -104,9 +105,10 @@
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns Nothing
          * @returns Nothing
          */
          */
-        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void>;
+        loadAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<void>;
 
 
         /**
         /**
          * The callback that returns true if the data can be directly loaded.
          * The callback that returns true if the data can be directly loaded.
@@ -124,9 +126,10 @@
          * @param data The data to import
          * @param data The data to import
          * @param rootUrl The root url for scene and resources
          * @param rootUrl The root url for scene and resources
          * @param onProgress The callback when the load progresses
          * @param onProgress The callback when the load progresses
+         * @param fullName Defines the FQDN of the file to load
          * @returns The loaded asset container
          * @returns The loaded asset container
          */
          */
-        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<AssetContainer>;
+        loadAssetContainerAsync(scene: Scene, data: string, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void, fullName?: string): Promise<AssetContainer>;
     }
     }
 
 
     interface IRegisteredPlugin {
     interface IRegisteredPlugin {
@@ -473,7 +476,7 @@
                 }
                 }
                 else {
                 else {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                    asyncedPlugin.importMeshAsync(meshNames, scene, data, rootUrl, progressHandler).then(result => {
+                    asyncedPlugin.importMeshAsync(meshNames, scene, data, rootUrl, progressHandler, rootUrl + sceneFilename).then(result => {
                         scene.loadingPluginName = plugin.name;
                         scene.loadingPluginName = plugin.name;
                         successHandler(result.meshes, result.particleSystems, result.skeletons, result.animationGroups);
                         successHandler(result.meshes, result.particleSystems, result.skeletons, result.animationGroups);
                     }).catch(error => {
                     }).catch(error => {
@@ -631,7 +634,7 @@
                     successHandler();
                     successHandler();
                 } else {
                 } else {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                    asyncedPlugin.loadAsync(scene, data, rootUrl, progressHandler).then(() => {
+                    asyncedPlugin.loadAsync(scene, data, rootUrl, progressHandler, rootUrl + sceneFilename).then(() => {
                         scene.loadingPluginName = plugin.name;
                         scene.loadingPluginName = plugin.name;
                         successHandler();
                         successHandler();
                     }).catch(error => {
                     }).catch(error => {
@@ -755,7 +758,7 @@
                     successHandler(assetContainer);
                     successHandler(assetContainer);
                 } else if ((<any>plugin).loadAssetContainerAsync) {
                 } else if ((<any>plugin).loadAssetContainerAsync) {
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
                     var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
-                    asyncedPlugin.loadAssetContainerAsync(scene, data, rootUrl, progressHandler).then(assetContainer => {
+                    asyncedPlugin.loadAssetContainerAsync(scene, data, rootUrl, progressHandler, rootUrl + sceneFilename).then(assetContainer => {
                         scene.loadingPluginName = plugin.name;
                         scene.loadingPluginName = plugin.name;
                         successHandler(assetContainer);
                         successHandler(assetContainer);
                     }).catch(error => {
                     }).catch(error => {

+ 9 - 55
src/Mesh/babylon.abstractMesh.ts

@@ -341,21 +341,11 @@
             this._markSubMeshesAsLightDirty();
             this._markSubMeshesAsLightDirty();
         }
         }
 
 
-        /**
-         * Gets or sets a boolean indicating if the outline must be rendered as well
-         * @see https://www.babylonjs-playground.com/#10WJ5S#3
-         */
-        public renderOutline = false;
         /** Defines color to use when rendering outline */
         /** Defines color to use when rendering outline */
         public outlineColor = Color3.Red();
         public outlineColor = Color3.Red();
         /** Define width to use when rendering outline */
         /** Define width to use when rendering outline */
         public outlineWidth = 0.02;
         public outlineWidth = 0.02;
 
 
-        /**
-         * Gets or sets a boolean indicating if the overlay must be rendered as well
-         * @see https://www.babylonjs-playground.com/#10WJ5S#2
-         */        
-        public renderOverlay = false;
         /** Defines color to use when rendering overlay */
         /** Defines color to use when rendering overlay */
         public overlayColor = Color3.Red();
         public overlayColor = Color3.Red();
         /** Defines alpha to use when rendering overlay */
         /** Defines alpha to use when rendering overlay */
@@ -534,7 +524,7 @@
          */
          */
         public edgesColor = new Color4(1, 0, 0, 1);
         public edgesColor = new Color4(1, 0, 0, 1);
         /** @hidden */
         /** @hidden */
-        public _edgesRenderer: Nullable<EdgesRenderer>;
+        public _edgesRenderer: Nullable<IEdgesRenderer>;
 
 
         // Cache
         // Cache
         private _collisionsTransformMatrix = Matrix.Zero();
         private _collisionsTransformMatrix = Matrix.Zero();
@@ -604,6 +594,11 @@
             return this._skeleton;
             return this._skeleton;
         }
         }
 
 
+        /**
+         * An event triggered when the mesh is rebuilt.
+         */
+        public onRebuildObservable = new Observable<AbstractMesh>();
+
         // Constructor
         // Constructor
 
 
         /**
         /**
@@ -647,14 +642,12 @@
 
 
         /** @hidden */
         /** @hidden */
         public _rebuild(): void {
         public _rebuild(): void {
+            this.onRebuildObservable.notifyObservers(this);
+
             if (this._occlusionQuery) {
             if (this._occlusionQuery) {
                 this._occlusionQuery = null;
                 this._occlusionQuery = null;
             }
             }
 
 
-            if (this._edgesRenderer) {
-                this._edgesRenderer._rebuild();
-            }
-
             if (!this.subMeshes) {
             if (!this.subMeshes) {
                 return;
                 return;
             }
             }
@@ -772,40 +765,6 @@
         }
         }
 
 
         // Methods
         // Methods
-
-        /**
-         * Disables the mesh edge rendering mode
-         * @returns the currentAbstractMesh
-         */
-        public disableEdgesRendering(): AbstractMesh {
-            if (this._edgesRenderer) {
-                this._edgesRenderer.dispose();
-                this._edgesRenderer = null;
-            }
-            return this;
-        }
-
-        /**
-         * Enables the edge rendering mode on the mesh.  
-         * This mode makes the mesh edges visible
-         * @param epsilon defines the maximal distance between two angles to detect a face
-         * @param checkVerticesInsteadOfIndices indicates that we should check vertex list directly instead of faces
-         * @returns the currentAbstractMesh 
-         * @see https://www.babylonjs-playground.com/#19O9TU#0
-         */
-        public enableEdgesRendering(epsilon = 0.95, checkVerticesInsteadOfIndices = false): AbstractMesh {
-            this.disableEdgesRendering();
-            this._edgesRenderer = new EdgesRenderer(this, epsilon, checkVerticesInsteadOfIndices);
-            return this;
-        }
-
-        /**
-         * Gets the edgesRenderer associated with the mesh
-         */
-        public get edgesRenderer(): Nullable<EdgesRenderer> {
-            return this._edgesRenderer;
-        }
-
         /**
         /**
          * Returns true if the mesh is blocked. Implemented by child classes
          * Returns true if the mesh is blocked. Implemented by child classes
          */
          */
@@ -1625,12 +1584,6 @@
                 }
                 }
             });
             });
 
 
-            // Edges
-            if (this._edgesRenderer) {
-                this._edgesRenderer.dispose();
-                this._edgesRenderer = null;
-            }
-
             // SubMeshes
             // SubMeshes
             if (this.getClassName() !== "InstancedMesh") {
             if (this.getClassName() !== "InstancedMesh") {
                 this.releaseSubMeshes();
                 this.releaseSubMeshes();
@@ -1684,6 +1637,7 @@
             this.onAfterWorldMatrixUpdateObservable.clear();
             this.onAfterWorldMatrixUpdateObservable.clear();
             this.onCollideObservable.clear();
             this.onCollideObservable.clear();
             this.onCollisionPositionChangeObservable.clear();
             this.onCollisionPositionChangeObservable.clear();
+            this.onRebuildObservable.clear();
 
 
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
             super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
         }

+ 0 - 14
src/Mesh/babylon.linesMesh.ts

@@ -139,19 +139,5 @@
         public clone(name: string, newParent?: Node, doNotCloneChildren?: boolean): LinesMesh {
         public clone(name: string, newParent?: Node, doNotCloneChildren?: boolean): LinesMesh {
             return new LinesMesh(name, this.getScene(), newParent, this, doNotCloneChildren);
             return new LinesMesh(name, this.getScene(), newParent, this, doNotCloneChildren);
         }
         }
-
-        /**
-         * Enables the edge rendering mode on the mesh.
-         * This mode makes the mesh edges visible
-         * @param epsilon defines the maximal distance between two angles to detect a face
-         * @param checkVerticesInsteadOfIndices indicates that we should check vertex list directly instead of faces
-         * @returns the currentAbstractMesh
-         * @see https://www.babylonjs-playground.com/#19O9TU#0
-         */
-        public enableEdgesRendering(epsilon = 0.95, checkVerticesInsteadOfIndices = false): AbstractMesh {
-            this.disableEdgesRendering();
-            this._edgesRenderer = new LineEdgesRenderer(this, epsilon, checkVerticesInsteadOfIndices);
-            return this;
-        }
     }
     }
 } 
 } 

+ 4 - 20
src/Mesh/babylon.mesh.ts

@@ -1415,12 +1415,8 @@
                 engine.setAlphaMode(this._effectiveMaterial.alphaMode);
                 engine.setAlphaMode(this._effectiveMaterial.alphaMode);
             }
             }
 
 
-            // Outline - step 1
-            var savedDepthWrite = engine.getDepthWrite();
-            if (this.renderOutline) {
-                engine.setDepthWrite(false);
-                scene.getOutlineRenderer().render(subMesh, batch);
-                engine.setDepthWrite(savedDepthWrite);
+            for (let step of scene._beforeRenderingMeshStage) {
+                step.action(this, subMesh, batch);
             }
             }
 
 
             var effect: Nullable<Effect>;
             var effect: Nullable<Effect>;
@@ -1475,20 +1471,8 @@
             // Unbind
             // Unbind
             this._effectiveMaterial.unbind();
             this._effectiveMaterial.unbind();
 
 
-            // Outline - step 2
-            if (this.renderOutline && savedDepthWrite) {
-                engine.setDepthWrite(true);
-                engine.setColorWrite(false);
-                scene.getOutlineRenderer().render(subMesh, batch);
-                engine.setColorWrite(true);
-            }
-
-            // Overlay
-            if (this.renderOverlay) {
-                var currentMode = engine.getAlphaMode();
-                engine.setAlphaMode(Engine.ALPHA_COMBINE);
-                scene.getOutlineRenderer().render(subMesh, batch, true);
-                engine.setAlphaMode(currentMode);
+            for (let step of scene._afterRenderingMeshStage) {
+                step.action(this, subMesh, batch);
             }
             }
 
 
             if (this._onAfterRenderObservable) {
             if (this._onAfterRenderObservable) {

+ 23 - 2
src/Particles/babylon.IParticleSystem.ts

@@ -107,7 +107,7 @@ module BABYLON {
         /**
         /**
          * The maximum number of particles to emit per frame until we reach the activeParticleCount value
          * The maximum number of particles to emit per frame until we reach the activeParticleCount value
          */
          */
-        emitRate: number; 
+        emitRate: number;
         
         
         /**
         /**
          * You can use gravity if you want to give an orientation to your particles.
          * You can use gravity if you want to give an orientation to your particles.
@@ -396,7 +396,28 @@ module BABYLON {
          * You must use addEmitRateGradient and removeEmitRateGradient to udpate this list
          * You must use addEmitRateGradient and removeEmitRateGradient to udpate this list
          * @returns the list of emit rate gradients
          * @returns the list of emit rate gradients
          */
          */
-        getEmitRateGradients(): Nullable<Array<FactorGradient>>;                    
+        getEmitRateGradients(): Nullable<Array<FactorGradient>>;   
+        
+        /**
+         * Adds a new start size gradient (please note that this will only work if you set the targetStopDuration property)
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the start size to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific start size gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeStartSizeGradient(gradient: number): IParticleSystem;    
+        /**
+         * Gets the current list of start size gradients.
+         * You must use addStartSizeGradient and removeStartSizeGradient to udpate this list
+         * @returns the list of start size gradients
+         */
+        getStartSizeGradients(): Nullable<Array<FactorGradient>>;  
         
         
 
 
         /**
         /**

+ 11 - 1
src/Particles/babylon.baseParticleSystem.ts

@@ -253,6 +253,7 @@ module BABYLON {
         protected _limitVelocityGradients: Nullable<Array<FactorGradient>> = null;
         protected _limitVelocityGradients: Nullable<Array<FactorGradient>> = null;
         protected _dragGradients: Nullable<Array<FactorGradient>> = null;
         protected _dragGradients: Nullable<Array<FactorGradient>> = null;
         protected _emitRateGradients: Nullable<Array<FactorGradient>> = null;
         protected _emitRateGradients: Nullable<Array<FactorGradient>> = null;
+        protected _startSizeGradients: Nullable<Array<FactorGradient>> = null;
 
 
         /**
         /**
          * Gets the current list of drag gradients.
          * Gets the current list of drag gradients.
@@ -321,13 +322,22 @@ module BABYLON {
         }         
         }         
 
 
         /**
         /**
+         * Gets the current list of start size gradients.
+         * You must use addStartSizeGradient and removeStartSizeGradient to udpate this list
+         * @returns the list of start size gradients
+         */
+        public getStartSizeGradients(): Nullable<Array<FactorGradient>> {
+            return this._startSizeGradients;
+        }
+        
+        /**
          * Gets the current list of emit rate gradients.
          * Gets the current list of emit rate gradients.
          * You must use addEmitRateGradient and removeEmitRateGradient to udpate this list
          * You must use addEmitRateGradient and removeEmitRateGradient to udpate this list
          * @returns the list of emit rate gradients
          * @returns the list of emit rate gradients
          */
          */
         public getEmitRateGradients(): Nullable<Array<FactorGradient>> {
         public getEmitRateGradients(): Nullable<Array<FactorGradient>> {
             return this._emitRateGradients;
             return this._emitRateGradients;
-        }             
+        }      
 
 
         /**
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.

+ 58 - 4
src/Particles/babylon.gpuParticleSystem.ts

@@ -453,6 +453,28 @@
         } 
         } 
 
 
         /**
         /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the start size value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+            return this;
+        }
+
+        /**
+         * Not supported by GPUParticleSystem
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeStartSizeGradient(gradient: number): IParticleSystem {
+            // Do nothing as start size is not supported by GPUParticleSystem
+            return this;
+        } 
+
+        /**
          * Instantiates a GPU particle system.
          * Instantiates a GPU particle system.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * Particles are often small sprites used to simulate hard-to-reproduce phenomena like fire, smoke, water, or abstract visual effects like magic glitter and faery dust.
          * @param name The name of the particle system
          * @param name The name of the particle system
@@ -497,9 +519,9 @@
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex"],
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex"],
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
-                                "angleRange", "radiusRange", "cellInfos", "noiseStrength"],
+                                "angleRange", "radiusRange", "cellInfos", "noiseStrength", "limitVelocityDamping"],
                 uniformBuffersNames: [],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "noiseSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "limitVelocityGradientSampler", "noiseSampler", "dragGradientSampler"],
                 defines: "",
                 defines: "",
                 fallbacks: null,  
                 fallbacks: null,  
                 onCompiled: null,
                 onCompiled: null,
@@ -751,7 +773,15 @@
             
             
             if (this._velocityGradientsTexture) {
             if (this._velocityGradientsTexture) {
                 defines += "\n#define VELOCITYGRADIENTS";
                 defines += "\n#define VELOCITYGRADIENTS";
-            }                    
+            }        
+
+            if (this._limitVelocityGradientsTexture) {
+                defines += "\n#define LIMITVELOCITYGRADIENTS";
+            }                
+            
+            if (this._dragGradientsTexture) {
+                defines += "\n#define DRAGGRADIENTS";
+            }              
             
             
             if (this.isAnimationSheetEnabled) {
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
                 defines += "\n#define ANIMATESHEET";
@@ -897,6 +927,14 @@
         private _createVelocityGradientTexture() {
         private _createVelocityGradientTexture() {
             this._createFactorGradientTexture(this._velocityGradients, "_velocityGradientsTexture");
             this._createFactorGradientTexture(this._velocityGradients, "_velocityGradientsTexture");
         }          
         }          
+
+        private _createLimitVelocityGradientTexture() {
+            this._createFactorGradientTexture(this._limitVelocityGradients, "_limitVelocityGradientsTexture");
+        }          
+
+        private _createDragGradientTexture() {
+            this._createFactorGradientTexture(this._dragGradients, "_dragGradientsTexture");
+        }            
             
             
         private _createColorGradientTexture() {
         private _createColorGradientTexture() {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
@@ -937,6 +975,8 @@
             this._createSizeGradientTexture();
             this._createSizeGradientTexture();
             this._createAngularSpeedGradientTexture();
             this._createAngularSpeedGradientTexture();
             this._createVelocityGradientTexture();
             this._createVelocityGradientTexture();
+            this._createLimitVelocityGradientTexture();
+            this._createDragGradientTexture();
 
 
             this._recreateUpdateEffect();
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
             this._recreateRenderEffect();
@@ -1008,6 +1048,15 @@
                 this._updateEffect.setTexture("velocityGradientSampler", this._velocityGradientsTexture);      
                 this._updateEffect.setTexture("velocityGradientSampler", this._velocityGradientsTexture);      
             }
             }
 
 
+            if (this._limitVelocityGradientsTexture) {      
+                this._updateEffect.setTexture("limitVelocityGradientSampler", this._limitVelocityGradientsTexture);  
+                this._updateEffect.setFloat("limitVelocityDamping", this.limitVelocityDamping);    
+            }            
+
+            if (this._dragGradientsTexture) {      
+                this._updateEffect.setTexture("dragGradientSampler", this._dragGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
             }
@@ -1194,7 +1243,12 @@
             if (this._limitVelocityGradientsTexture) {
             if (this._limitVelocityGradientsTexture) {
                 this._limitVelocityGradientsTexture.dispose();
                 this._limitVelocityGradientsTexture.dispose();
                 (<any>this._limitVelocityGradientsTexture) = null;
                 (<any>this._limitVelocityGradientsTexture) = null;
-            }         
+            }                   
+
+            if (this._dragGradientsTexture) {
+                this._dragGradientsTexture.dispose();
+                (<any>this._dragGradientsTexture) = null;
+            }               
          
          
             if (this._randomTexture) {
             if (this._randomTexture) {
                 this._randomTexture.dispose();
                 this._randomTexture.dispose();

+ 88 - 4
src/Particles/babylon.particleSystem.ts

@@ -75,7 +75,14 @@
         /** @hidden */
         /** @hidden */
         public _currentEmitRate1 = 0;
         public _currentEmitRate1 = 0;
         /** @hidden */
         /** @hidden */
-        public _currentEmitRate2 = 0;              
+        public _currentEmitRate2 = 0;   
+        
+        /** @hidden */
+        public _currentStartSizeGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentStartSize1 = 0;
+        /** @hidden */
+        public _currentStartSize2 = 0;   
 
 
         // end of sheet animation
         // end of sheet animation
 
 
@@ -532,7 +539,45 @@
             this._removeFactorGradient(this._emitRateGradients, gradient);
             this._removeFactorGradient(this._emitRateGradients, gradient);
 
 
             return this;
             return this;
-        }           
+        }    
+        
+        /**
+         * Adds a new start size gradient (please note that this will only work if you set the targetStopDuration property)
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the start size value to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addStartSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem {
+            if (!this._startSizeGradients) {
+                this._startSizeGradients = [];
+            }
+
+            this._addFactorGradient(this._startSizeGradients, gradient, factor, factor2);
+
+            if (!this._currentStartSizeGradient) {
+                this._currentStartSizeGradient = this._startSizeGradients[0];
+                this._currentStartSize1 = this._currentStartSizeGradient.getFactor();
+                this._currentStartSize2 = this._currentStartSize1;
+            }
+
+            if (this._startSizeGradients.length === 2) {
+                this._currentStartSize2 = this._startSizeGradients[1].getFactor();
+            }
+
+            return this;
+        }
+
+        /**
+         * Remove a specific start size gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeStartSizeGradient(gradient: number): IParticleSystem {
+            this._removeFactorGradient(this._emitRateGradients, gradient);
+
+            return this;
+        } 
 
 
         /**
         /**
          * Adds a new color gradient
          * Adds a new color gradient
@@ -961,6 +1006,21 @@
                 }
                 }
                 // Size and scale
                 // Size and scale
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
+                
+                // Adjust scale by start size
+                if(this._startSizeGradients && this._startSizeGradients[0]){
+                    const ratio = this._actualFrame / this.targetStopDuration;            
+                    Tools.GetCurrentGradient(ratio, this._startSizeGradients, (currentGradient, nextGradient, scale) => {
+                        if (currentGradient !== this._currentStartSizeGradient) {
+                            this._currentStartSize1 = this._currentStartSize2;
+                            this._currentStartSize2 = (<FactorGradient>nextGradient).getFactor();    
+                            this._currentStartSizeGradient = (<FactorGradient>currentGradient);
+                        }                                
+                        
+                        var value = Scalar.Lerp(this._currentStartSize1, this._currentStartSize2, scale);
+                        particle.scale.scaleInPlace(value);
+                    });
+                }
 
 
                 // Angle
                 // Angle
                 if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
                 if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
@@ -1621,7 +1681,25 @@
 
 
                     serializationObject.emitRateGradients.push(serializedGradient);
                     serializationObject.emitRateGradients.push(serializedGradient);
                 }
                 }
-            }                
+            } 
+            
+            let startSizeGradients = particleSystem.getStartSizeGradients();
+            if (startSizeGradients) {
+                serializationObject.startSizeGradients = [];
+                for (var startSizeGradient of startSizeGradients) {
+
+                    var serializedGradient: any = {
+                        gradient: startSizeGradient.gradient,
+                        factor1: startSizeGradient.factor1
+                    };
+
+                    if (startSizeGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = startSizeGradient.factor2;
+                    }
+
+                    serializationObject.startSizeGradients.push(serializedGradient);
+                }
+            } 
 
 
             let limitVelocityGradients = particleSystem.getLimitVelocityGradients();
             let limitVelocityGradients = particleSystem.getLimitVelocityGradients();
             if (limitVelocityGradients) {
             if (limitVelocityGradients) {
@@ -1762,7 +1840,13 @@
                 for (var emitRateGradient of parsedParticleSystem.emitRateGradients) {
                 for (var emitRateGradient of parsedParticleSystem.emitRateGradients) {
                     particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ?  emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
                     particleSystem.addEmitRateGradient(emitRateGradient.gradient, emitRateGradient.factor1 !== undefined ?  emitRateGradient.factor1 : emitRateGradient.factor, emitRateGradient.factor2);
                 }
                 }
-            }               
+            } 
+            
+            if (parsedParticleSystem.startSizeGradients) {
+                for (var startSizeGradient of parsedParticleSystem.startSizeGradients) {
+                    particleSystem.addStartSizeGradient(startSizeGradient.gradient, startSizeGradient.factor1 !== undefined ?  startSizeGradient.factor1 : startSizeGradient.factor, startSizeGradient.factor2);
+                }
+            }
 
 
             if (parsedParticleSystem.limitVelocityGradients) {
             if (parsedParticleSystem.limitVelocityGradients) {
                 for (var limitVelocityGradient of parsedParticleSystem.limitVelocityGradients) {
                 for (var limitVelocityGradient of parsedParticleSystem.limitVelocityGradients) {

+ 9 - 3
src/Physics/Plugins/babylon.cannonJSPlugin.ts

@@ -205,14 +205,20 @@
             if (impostorJoint.joint.type !== PhysicsJoint.SpringJoint) {
             if (impostorJoint.joint.type !== PhysicsJoint.SpringJoint) {
                 this.world.addConstraint(constraint);
                 this.world.addConstraint(constraint);
             } else {
             } else {
-                impostorJoint.mainImpostor.registerAfterPhysicsStep(function () {
+                (<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback = (<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback || function () {
                     constraint.applyForce();
                     constraint.applyForce();
-                });
+                };
+                impostorJoint.mainImpostor.registerAfterPhysicsStep((<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback);
             }
             }
         }
         }
 
 
         public removeJoint(impostorJoint: PhysicsImpostorJoint) {
         public removeJoint(impostorJoint: PhysicsImpostorJoint) {
-            this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            if (impostorJoint.joint.type !== PhysicsJoint.SpringJoint) {
+                this.world.removeConstraint(impostorJoint.joint.physicsJoint);
+            } else {
+                impostorJoint.mainImpostor.unregisterAfterPhysicsStep((<SpringJointData>impostorJoint.joint.jointData).forceApplicationCallback);
+            }
+
         }
         }
 
 
         private _addMaterial(name: string, friction: number, restitution: number) {
         private _addMaterial(name: string, friction: number, restitution: number) {

+ 19 - 17
src/Physics/babylon.physicsJoint.ts

@@ -40,13 +40,13 @@ module BABYLON {
         public set physicsPlugin(physicsPlugin: IPhysicsEnginePlugin) {
         public set physicsPlugin(physicsPlugin: IPhysicsEnginePlugin) {
             this._physicsPlugin = physicsPlugin;
             this._physicsPlugin = physicsPlugin;
         }
         }
-        
+
         /**
         /**
          * Execute a function that is physics-plugin specific.
          * Execute a function that is physics-plugin specific.
          * @param {Function} func the function that will be executed. 
          * @param {Function} func the function that will be executed. 
          *                        It accepts two parameters: the physics world and the physics joint.
          *                        It accepts two parameters: the physics world and the physics joint.
          */
          */
-        public executeNativeFunction(func : (world: any, physicsJoint:any) => void) {
+        public executeNativeFunction(func: (world: any, physicsJoint: any) => void) {
             func(this._physicsPlugin.world, this._physicsJoint)
             func(this._physicsPlugin.world, this._physicsJoint)
         }
         }
 
 
@@ -87,13 +87,13 @@ module BABYLON {
             this._physicsPlugin.updateDistanceJoint(this, maxDistance, minDistance);
             this._physicsPlugin.updateDistanceJoint(this, maxDistance, minDistance);
         }
         }
     }
     }
-    
+
     export class MotorEnabledJoint extends PhysicsJoint implements IMotorEnabledJoint {
     export class MotorEnabledJoint extends PhysicsJoint implements IMotorEnabledJoint {
-        
-        constructor(type: number, jointData:PhysicsJointData) {
+
+        constructor(type: number, jointData: PhysicsJointData) {
             super(type, jointData);
             super(type, jointData);
         }
         }
-        
+
         /**
         /**
          * Set the motor values.
          * Set the motor values.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -103,7 +103,7 @@ module BABYLON {
         public setMotor(force?: number, maxForce?: number) {
         public setMotor(force?: number, maxForce?: number) {
             this._physicsPlugin.setMotor(this, force || 0, maxForce);
             this._physicsPlugin.setMotor(this, force || 0, maxForce);
         }
         }
-        
+
         /**
         /**
          * Set the motor's limits.
          * Set the motor's limits.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -117,11 +117,11 @@ module BABYLON {
      * This class represents a single hinge physics joint
      * This class represents a single hinge physics joint
      */
      */
     export class HingeJoint extends MotorEnabledJoint {
     export class HingeJoint extends MotorEnabledJoint {
-        
-        constructor(jointData:PhysicsJointData) {
+
+        constructor(jointData: PhysicsJointData) {
             super(PhysicsJoint.HingeJoint, jointData);
             super(PhysicsJoint.HingeJoint, jointData);
         }
         }
-        
+
         /**
         /**
          * Set the motor values.
          * Set the motor values.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -131,7 +131,7 @@ module BABYLON {
         public setMotor(force?: number, maxForce?: number) {
         public setMotor(force?: number, maxForce?: number) {
             this._physicsPlugin.setMotor(this, force || 0, maxForce);
             this._physicsPlugin.setMotor(this, force || 0, maxForce);
         }
         }
-        
+
         /**
         /**
          * Set the motor's limits.
          * Set the motor's limits.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -140,16 +140,16 @@ module BABYLON {
             this._physicsPlugin.setLimit(this, upperLimit, lowerLimit);
             this._physicsPlugin.setLimit(this, upperLimit, lowerLimit);
         }
         }
     }
     }
-    
+
     /**
     /**
      * This class represents a dual hinge physics joint (same as wheel joint)
      * This class represents a dual hinge physics joint (same as wheel joint)
      */
      */
     export class Hinge2Joint extends MotorEnabledJoint {
     export class Hinge2Joint extends MotorEnabledJoint {
-        
-        constructor(jointData:PhysicsJointData) {
+
+        constructor(jointData: PhysicsJointData) {
             super(PhysicsJoint.Hinge2Joint, jointData);
             super(PhysicsJoint.Hinge2Joint, jointData);
         }
         }
-        
+
         /**
         /**
          * Set the motor values.
          * Set the motor values.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -160,7 +160,7 @@ module BABYLON {
         public setMotor(force?: number, maxForce?: number, motorIndex: number = 0) {
         public setMotor(force?: number, maxForce?: number, motorIndex: number = 0) {
             this._physicsPlugin.setMotor(this, force || 0, maxForce, motorIndex);
             this._physicsPlugin.setMotor(this, force || 0, maxForce, motorIndex);
         }
         }
-        
+
         /**
         /**
          * Set the motor limits.
          * Set the motor limits.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
          * Attention, this function is plugin specific. Engines won't react 100% the same.
@@ -189,5 +189,7 @@ module BABYLON {
         length: number;
         length: number;
         stiffness: number;
         stiffness: number;
         damping: number;
         damping: number;
+        /** this callback will be called when applying the force to the impostors. */
+        forceApplicationCallback: () => void;
     }
     }
-}
+}

+ 110 - 2
src/Rendering/babylon.edgesRenderer.ts

@@ -1,4 +1,67 @@
 module BABYLON {
 module BABYLON {
+    export interface AbstractMesh {
+        /**
+         * Disables the mesh edge rendering mode
+         * @returns the currentAbstractMesh
+         */
+        disableEdgesRendering(): AbstractMesh;
+        
+        /**
+         * Enables the edge rendering mode on the mesh.  
+         * This mode makes the mesh edges visible
+         * @param epsilon defines the maximal distance between two angles to detect a face
+         * @param checkVerticesInsteadOfIndices indicates that we should check vertex list directly instead of faces
+         * @returns the currentAbstractMesh 
+         * @see https://www.babylonjs-playground.com/#19O9TU#0
+         */
+        enableEdgesRendering(epsilon?: number, checkVerticesInsteadOfIndices?: boolean): AbstractMesh;
+        
+        /**
+         * Gets the edgesRenderer associated with the mesh
+         */
+        edgesRenderer: Nullable<EdgesRenderer>;
+    }
+
+    AbstractMesh.prototype.disableEdgesRendering = function(): AbstractMesh {
+        if (this._edgesRenderer) {
+            this._edgesRenderer.dispose();
+            this._edgesRenderer = null;
+        }
+        return this;
+    }
+
+    AbstractMesh.prototype.enableEdgesRendering = function(epsilon = 0.95, checkVerticesInsteadOfIndices = false): AbstractMesh {
+        this.disableEdgesRendering();
+        this._edgesRenderer = new EdgesRenderer(this, epsilon, checkVerticesInsteadOfIndices);
+        return this;
+    }
+
+    Object.defineProperty(AbstractMesh.prototype, "edgesRenderer", {
+        get: function (this:AbstractMesh) {
+            return this._edgesRenderer;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    export interface LinesMesh {
+        /**
+         * Enables the edge rendering mode on the mesh.
+         * This mode makes the mesh edges visible
+         * @param epsilon defines the maximal distance between two angles to detect a face
+         * @param checkVerticesInsteadOfIndices indicates that we should check vertex list directly instead of faces
+         * @returns the currentAbstractMesh
+         * @see https://www.babylonjs-playground.com/#19O9TU#0
+         */
+        enableEdgesRendering(epsilon?: number, checkVerticesInsteadOfIndices?: boolean): AbstractMesh;
+    }
+
+    LinesMesh.prototype.enableEdgesRendering = function(epsilon = 0.95, checkVerticesInsteadOfIndices = false): AbstractMesh {
+        this.disableEdgesRendering();
+        this._edgesRenderer = new LineEdgesRenderer(this, epsilon, checkVerticesInsteadOfIndices);
+        return this;
+    }
+
     /**
     /**
      * FaceAdjacencies Helper class to generate edges
      * FaceAdjacencies Helper class to generate edges
      */
      */
@@ -11,9 +74,30 @@
     }
     }
 
 
     /**
     /**
+     * Defines the minimum contract an Edges renderer should follow.
+     */
+    export interface IEdgesRenderer extends IDisposable {
+        /** 
+         * Gets or sets a boolean indicating if the edgesRenderer is active 
+         */
+        isEnabled: boolean;
+
+        /**
+         * Renders the edges of the attached mesh,
+         */
+        render(): void;
+
+        /**
+         * Checks wether or not the edges renderer is ready to render.
+         * @return true if ready, otherwise false.
+         */
+        isReady(): boolean;
+    }
+
+    /**
      * This class is used to generate edges of the mesh that could then easily be rendered in a scene.
      * This class is used to generate edges of the mesh that could then easily be rendered in a scene.
      */
      */
-    export class EdgesRenderer {
+    export class EdgesRenderer implements IEdgesRenderer {
         public edgesWidthScalerForOrthographic = 1000.0;
         public edgesWidthScalerForOrthographic = 1000.0;
         public edgesWidthScalerForPerspective = 50.0;
         public edgesWidthScalerForPerspective = 50.0;
         protected _source: AbstractMesh;
         protected _source: AbstractMesh;
@@ -28,6 +112,9 @@
         protected _buffers: { [key: string]: Nullable<VertexBuffer> } = {};
         protected _buffers: { [key: string]: Nullable<VertexBuffer> } = {};
         protected _checkVerticesInsteadOfIndices = false;
         protected _checkVerticesInsteadOfIndices = false;
 
 
+        private _meshRebuildObserver: Nullable<Observer<AbstractMesh>>;
+        private _meshDisposeObserver: Nullable<Observer<Node>>;
+
         /** Gets or sets a boolean indicating if the edgesRenderer is active */
         /** Gets or sets a boolean indicating if the edgesRenderer is active */
         public isEnabled = true;
         public isEnabled = true;
 
 
@@ -49,6 +136,14 @@
             if(generateEdgesLines) {
             if(generateEdgesLines) {
                 this._generateEdgesLines();
                 this._generateEdgesLines();
             }
             }
+
+            this._meshRebuildObserver = this._source.onRebuildObservable.add(() => {
+                this._rebuild();
+            });
+
+            this._meshDisposeObserver = this._source.onDisposeObservable.add(() => {
+                this.dispose();
+            });
         }
         }
 
 
         protected _prepareRessources(): void {
         protected _prepareRessources(): void {
@@ -87,6 +182,8 @@
          * Releases the required resources for the edges renderer
          * Releases the required resources for the edges renderer
          */
          */
         public dispose(): void {
         public dispose(): void {
+            this._source.onRebuildObservable.remove(this._meshRebuildObserver);
+            this._source.onDisposeObservable.remove(this._meshDisposeObserver);
 
 
             var buffer = this._buffers[VertexBuffer.PositionKind];
             var buffer = this._buffers[VertexBuffer.PositionKind];
             if (buffer) {
             if (buffer) {
@@ -332,10 +429,21 @@
             this._indicesCount = this._linesIndices.length;
             this._indicesCount = this._linesIndices.length;
         }
         }
 
 
+        /**
+         * Checks wether or not the edges renderer is ready to render.
+         * @return true if ready, otherwise false.
+         */
+        public isReady(): boolean {
+            return this._lineShader.isReady();
+        }
+
+        /**
+         * Renders the edges of the attached mesh,
+         */
         public render(): void {
         public render(): void {
             var scene = this._source.getScene();
             var scene = this._source.getScene();
 
 
-            if (!this._lineShader.isReady() || !scene.activeCamera) {
+            if (!this.isReady() || !scene.activeCamera) {
                 return;
                 return;
             }
             }
 
 

+ 166 - 8
src/Rendering/babylon.outlineRenderer.ts

@@ -1,18 +1,141 @@
 module BABYLON {
 module BABYLON {
-    export class OutlineRenderer {
-        private _scene: Scene;
-        private _effect: Effect;
-        private _cachedDefines: string;
+    export interface Scene {
+        /** @hidden */
+        _outlineRenderer: OutlineRenderer;
 
 
+        /** 
+         * Gets the outline renderer associated with the scene
+         * @returns a OutlineRenderer
+         */
+        getOutlineRenderer(): OutlineRenderer;
+    }
+
+    /** 
+     * Gets the outline renderer associated with the scene
+     * @returns a OutlineRenderer
+     */
+    Scene.prototype.getOutlineRenderer = function(): OutlineRenderer {
+        if (!this._outlineRenderer) {
+            this._outlineRenderer = new OutlineRenderer(this);
+        }
+        return this._outlineRenderer;
+    }
+
+    export interface AbstractMesh {
+        /** @hidden (Backing field) */
+        _renderOutline: boolean;
+        /**
+         * Gets or sets a boolean indicating if the outline must be rendered as well
+         * @see https://www.babylonjs-playground.com/#10WJ5S#3
+         */
+        renderOutline: boolean;
+
+        /** @hidden (Backing field) */
+        _renderOverlay: boolean;
+        /**
+         * Gets or sets a boolean indicating if the overlay must be rendered as well
+         * @see https://www.babylonjs-playground.com/#10WJ5S#2
+         */
+        renderOverlay: boolean;
+    }
+
+    Object.defineProperty(AbstractMesh.prototype, "renderOutline", {
+        get: function (this:AbstractMesh) {
+            return this._renderOutline;
+        },
+        set: function (this:AbstractMesh, value: boolean) {
+            if (value) {
+                // Lazy Load the component.
+                this.getScene().getOutlineRenderer();
+            }
+            this._renderOutline = value;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    Object.defineProperty(AbstractMesh.prototype, "renderOverlay", {
+        get: function (this:AbstractMesh) {
+            return this._renderOverlay;
+        },
+        set: function (this:AbstractMesh, value: boolean) {
+            if (value) {
+                // Lazy Load the component.
+                this.getScene().getOutlineRenderer();
+            }
+            this._renderOverlay = value;
+        },
+        enumerable: true,
+        configurable: true
+    });
+
+    /**
+     * This class is responsible to draw bothe outline/overlay of meshes.
+     * It should not be used directly but through the available method on mesh.
+     */
+    export class OutlineRenderer implements ISceneComponent {
+        /**
+         * The name of the component. Each component must have a unique name.
+         */
+        public name = SceneComponentConstants.NAME_OUTLINERENDERER;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /**
+         * Defines a zOffset to prevent zFighting between the overlay and the mesh.
+         */
         public zOffset = 1;
         public zOffset = 1;
 
 
+        private _engine: Engine;
+        private _effect: Effect;
+        private _cachedDefines: string;
+        private _savedDepthWrite: boolean;
+
+        /**
+         * Instantiates a new outline renderer. (There could be only one per scene).
+         * @param scene Defines the scene it belongs to
+         */
         constructor(scene: Scene) {
         constructor(scene: Scene) {
-            this._scene = scene;
+            this.scene = scene;
+            this._engine = scene.getEngine();
+            this.scene._addComponent(this);
+        }
+
+        /**
+         * Register the component to one instance of a scene.
+         */
+        public register(): void {
+            this.scene._beforeRenderingMeshStage.registerStep(SceneComponentConstants.STEP_BEFORERENDERINGMESH_OUTLINE, this, this._beforeRenderingMesh);
+            this.scene._afterRenderingMeshStage.registerStep(SceneComponentConstants.STEP_AFTERRENDERINGMESH_OUTLINE, this, this._afterRenderingMesh);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            // Nothing to do here.
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            // Nothing to do here.
         }
         }
 
 
+        /**
+         * Renders the outline in the canvas.
+         * @param subMesh Defines the sumesh to render
+         * @param batch Defines the batch of meshes in case of instances
+         * @param useOverlay Defines if the rendering is for the overlay or the outline
+         */
         public render(subMesh: SubMesh, batch: _InstancesBatch, useOverlay: boolean = false): void {
         public render(subMesh: SubMesh, batch: _InstancesBatch, useOverlay: boolean = false): void {
-            var scene = this._scene;
-            var engine = this._scene.getEngine();
+            var scene = this.scene;
+            var engine = scene.getEngine();
 
 
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
 
 
@@ -63,6 +186,13 @@
             engine.setZOffset(0);
             engine.setZOffset(0);
         }
         }
 
 
+        /**
+         * Returns whether or not the outline renderer is ready for a given submesh.
+         * All the dependencies e.g. submeshes, texture, effect... mus be ready
+         * @param subMesh Defines the submesh to check readyness for
+         * @param useInstances Defines wheter wee are trying to render instances or not
+         * @returns true if ready otherwise false
+         */
         public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
         public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
             var defines = [];
             var defines = [];
             var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
             var attribs = [VertexBuffer.PositionKind, VertexBuffer.NormalKind];
@@ -117,7 +247,7 @@
             var join = defines.join("\n");
             var join = defines.join("\n");
             if (this._cachedDefines !== join) {
             if (this._cachedDefines !== join) {
                 this._cachedDefines = join;
                 this._cachedDefines = join;
-                this._effect = this._scene.getEngine().createEffect("outline",
+                this._effect = this.scene.getEngine().createEffect("outline",
                     attribs,
                     attribs,
                     ["world", "mBones", "viewProjection", "diffuseMatrix", "offset", "color", "logarithmicDepthConstant"],
                     ["world", "mBones", "viewProjection", "diffuseMatrix", "offset", "color", "logarithmicDepthConstant"],
                     ["diffuseSampler"], join);
                     ["diffuseSampler"], join);
@@ -125,5 +255,33 @@
 
 
             return this._effect.isReady();
             return this._effect.isReady();
         }
         }
+
+        private _beforeRenderingMesh(mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch): void {
+            // Outline - step 1
+            this._savedDepthWrite = this._engine.getDepthWrite();
+            if (mesh.renderOutline) {
+                this._engine.setDepthWrite(false);
+                this.render(subMesh, batch);
+                this._engine.setDepthWrite(this._savedDepthWrite);
+            }
+        }
+
+        private _afterRenderingMesh(mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch): void {
+            // Outline - step 2
+            if (mesh.renderOutline && this._savedDepthWrite) {
+                this._engine.setDepthWrite(true);
+                this._engine.setColorWrite(false);
+                this.render(subMesh, batch);
+                this._engine.setColorWrite(true);
+            }
+
+            // Overlay
+            if (mesh.renderOverlay) {
+                var currentMode = this._engine.getAlphaMode();
+                this._engine.setAlphaMode(Engine.ALPHA_COMBINE);
+                this.render(subMesh, batch, true);
+                this._engine.setAlphaMode(currentMode);
+            }
+        }
     }
     }
 } 
 } 

+ 4 - 4
src/Rendering/babylon.renderingGroup.ts

@@ -6,7 +6,7 @@
         private _alphaTestSubMeshes = new SmartArray<SubMesh>(256);
         private _alphaTestSubMeshes = new SmartArray<SubMesh>(256);
         private _depthOnlySubMeshes = new SmartArray<SubMesh>(256);
         private _depthOnlySubMeshes = new SmartArray<SubMesh>(256);
         private _particleSystems = new SmartArray<IParticleSystem>(256);
         private _particleSystems = new SmartArray<IParticleSystem>(256);
-        private _spriteManagers = new SmartArray<SpriteManager>(256);
+        private _spriteManagers = new SmartArray<ISpriteManager>(256);
 
 
         private _opaqueSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
         private _opaqueSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
         private _alphaTestSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
         private _alphaTestSortCompareFn: Nullable<(a: SubMesh, b: SubMesh) => number>;
@@ -16,7 +16,7 @@
         private _renderAlphaTest: (subMeshes: SmartArray<SubMesh>) => void;
         private _renderAlphaTest: (subMeshes: SmartArray<SubMesh>) => void;
         private _renderTransparent: (subMeshes: SmartArray<SubMesh>) => void;
         private _renderTransparent: (subMeshes: SmartArray<SubMesh>) => void;
 
 
-        private _edgesRenderers = new SmartArray<EdgesRenderer>(16);
+        private _edgesRenderers = new SmartArray<IEdgesRenderer>(16);
 
 
         public onBeforeTransparentRendering: () => void;
         public onBeforeTransparentRendering: () => void;
 
 
@@ -345,12 +345,12 @@
                 this._opaqueSubMeshes.push(subMesh); // Opaque
                 this._opaqueSubMeshes.push(subMesh); // Opaque
             }
             }
 
 
-            if (mesh._edgesRenderer !== null && mesh._edgesRenderer !== undefined && mesh._edgesRenderer.isEnabled) {
+            if (mesh._edgesRenderer && mesh._edgesRenderer.isEnabled) {
                 this._edgesRenderers.push(mesh._edgesRenderer);
                 this._edgesRenderers.push(mesh._edgesRenderer);
             }
             }
         }
         }
 
 
-        public dispatchSprites(spriteManager: SpriteManager) {
+        public dispatchSprites(spriteManager: ISpriteManager) {
             this._spriteManagers.push(spriteManager);
             this._spriteManagers.push(spriteManager);
         }
         }
 
 

+ 2 - 2
src/Rendering/babylon.renderingManager.ts

@@ -76,7 +76,7 @@
             info.camera = this._scene.activeCamera;
             info.camera = this._scene.activeCamera;
 
 
             // Dispatch sprites
             // Dispatch sprites
-            if (renderSprites) {
+            if (this._scene.spriteManagers && renderSprites) {
                 for (let index = 0; index < this._scene.spriteManagers.length; index++) {
                 for (let index = 0; index < this._scene.spriteManagers.length; index++) {
                     var manager = this._scene.spriteManagers[index];
                     var manager = this._scene.spriteManagers[index];
                     this.dispatchSprites(manager);
                     this.dispatchSprites(manager);
@@ -158,7 +158,7 @@
             }
             }
         }
         }
 
 
-        public dispatchSprites(spriteManager: SpriteManager) {
+        public dispatchSprites(spriteManager: ISpriteManager) {
             var renderingGroupId = spriteManager.renderingGroupId || 0;
             var renderingGroupId = spriteManager.renderingGroupId || 0;
 
 
             this._prepareRenderingGroup(renderingGroupId);
             this._prepareRenderingGroup(renderingGroupId);

+ 2 - 1
src/Shaders/gpuRenderParticles.fragment.fx

@@ -21,7 +21,8 @@ void main() {
   	outFragColor = textureColor * vColor;
   	outFragColor = textureColor * vColor;
 
 
 	#ifdef BLENDMULTIPLYMODE
 	#ifdef BLENDMULTIPLYMODE
-	outFragColor.rgb += vec3(1.0 - textureColor.a);
+	float alpha = vColor.a * textureColor.a;
+	outFragColor.rgb = outFragColor.rgb * alpha + vec3(1.0) * (1.0 - alpha);	
 	#endif	  
 	#endif	  
 
 
 // Apply image processing if relevant. As this applies in linear space, 
 // Apply image processing if relevant. As this applies in linear space, 

+ 28 - 1
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -123,6 +123,15 @@ uniform sampler2D angularSpeedGradientSampler;
 uniform sampler2D velocityGradientSampler;
 uniform sampler2D velocityGradientSampler;
 #endif
 #endif
 
 
+#ifdef LIMITVELOCITYGRADIENTS
+uniform sampler2D limitVelocityGradientSampler;
+uniform float limitVelocityDamping;
+#endif
+
+#ifdef DRAGGRADIENTS
+uniform sampler2D dragGradientSampler;
+#endif
+
 #ifdef NOISE
 #ifdef NOISE
 uniform vec3 noiseStrength;
 uniform vec3 noiseStrength;
 uniform sampler2D noiseSampler;
 uniform sampler2D noiseSampler;
@@ -309,6 +318,11 @@ void main() {
 #ifdef VELOCITYGRADIENTS
 #ifdef VELOCITYGRADIENTS
     directionScale *= texture(velocityGradientSampler, vec2(ageGradient, 0)).r;
     directionScale *= texture(velocityGradientSampler, vec2(ageGradient, 0)).r;
 #endif
 #endif
+
+#ifdef DRAGGRADIENTS
+    directionScale *= texture(dragGradientSampler, vec2(ageGradient, 0)).r;
+#endif
+
     outPosition = position + direction * directionScale;
     outPosition = position + direction * directionScale;
     
     
     outLife = life;
     outLife = life;
@@ -327,7 +341,20 @@ void main() {
 #ifndef BILLBOARD    
 #ifndef BILLBOARD    
     outInitialDirection = initialDirection;
     outInitialDirection = initialDirection;
 #endif
 #endif
-    outDirection = direction + gravity * timeDelta;
+
+    vec3 updatedDirection = direction + gravity * timeDelta;
+
+#ifdef LIMITVELOCITYGRADIENTS
+    float limitVelocity = texture(limitVelocityGradientSampler, vec2(ageGradient, 0)).r;
+
+    float currentVelocity = length(updatedDirection);
+
+    if (currentVelocity > limitVelocity) {
+        updatedDirection = updatedDirection * limitVelocityDamping;
+    }
+#endif
+
+    outDirection = updatedDirection;
 
 
 #ifdef NOISE
 #ifdef NOISE
     vec3 localPosition = outPosition - emitterWM[3].xyz;
     vec3 localPosition = outPosition - emitterWM[3].xyz;

+ 4 - 4
src/Shaders/particles.fragment.fx

@@ -15,12 +15,12 @@ uniform sampler2D diffuseSampler;
 void main(void) {
 void main(void) {
 	#include<clipPlaneFragment>
 	#include<clipPlaneFragment>
 
 
-	vec4 baseColor = texture2D(diffuseSampler, vUV);
-	float alpha = baseColor.a;
-	baseColor = (baseColor * textureMask + (vec4(1., 1., 1., 1.) - textureMask)) * vColor;
+	vec4 textureColor = texture2D(diffuseSampler, vUV);
+	vec4 baseColor = (textureColor * textureMask + (vec4(1., 1., 1., 1.) - textureMask)) * vColor;
 
 
 	#ifdef BLENDMULTIPLYMODE
 	#ifdef BLENDMULTIPLYMODE
-	baseColor.rgb += vec3(1.0 - alpha);
+	float alpha = vColor.a * textureColor.a;
+	baseColor.rgb = baseColor.rgb * alpha + vec3(1.0) * (1.0 - alpha);
 	#endif
 	#endif
 
 
 // Apply image processing if relevant. As this applies in linear space, 
 // Apply image processing if relevant. As this applies in linear space, 

+ 2 - 2
src/Sprites/babylon.sprite.ts

@@ -19,7 +19,7 @@
         private _toIndex = 0;
         private _toIndex = 0;
         private _delay = 0;
         private _delay = 0;
         private _direction = 1;
         private _direction = 1;
-        private _manager: SpriteManager;
+        private _manager: ISpriteManager;
         private _time = 0;
         private _time = 0;
         private _onAnimationEnd: () => void;
         private _onAnimationEnd: () => void;
         /**
         /**
@@ -36,7 +36,7 @@
             this.height = value;
             this.height = value;
         }
         }
 
 
-        constructor(public name: string, manager: SpriteManager) {
+        constructor(public name: string, manager: ISpriteManager) {
             this._manager = manager;
             this._manager = manager;
 
 
             this._manager.sprites.push(this);
             this._manager.sprites.push(this);

+ 46 - 1
src/Sprites/babylon.spriteManager.ts

@@ -1,5 +1,47 @@
 module BABYLON {
 module BABYLON {
-    export class SpriteManager {
+    /**
+     * Defines the minimum interface to fullfil in order to be a sprite manager.
+     */
+    export interface ISpriteManager extends IDisposable {
+        /**
+         * Restricts the camera to viewing objects with the same layerMask.
+         * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
+         */
+        layerMask: number;
+
+        /**
+         * Gets or sets a boolean indicating if the mesh can be picked (by scene.pick for instance or through actions). Default is true
+         */
+        isPickable: boolean;
+
+        /** 
+         * Specifies the rendering group id for this mesh (0 by default) 
+         * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
+         */
+        renderingGroupId: number;
+
+        /**
+         * Defines the list of sprites managed by the manager.
+         */
+        sprites: Array<Sprite>;
+
+        /**
+         * Tests the intersection of a sprite with a specific ray.
+         * @param ray The ray we are sending to test the collision
+         * @param camera The camera space we are sending rays in
+         * @param predicate A predicate allowing excluding sprites from the list of object to test
+         * @param fastCheck Is the hit test done in a OOBB or AOBB fashion the faster, the less precise
+         * @returns picking info or null.
+         */
+        intersects(ray: Ray, camera:Camera, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
+
+        /**
+         * Renders the list of sprites on screen.
+         */
+        render(): void;
+    }
+
+    export class SpriteManager implements ISpriteManager {
         public sprites = new Array<Sprite>();
         public sprites = new Array<Sprite>();
         public renderingGroupId = 0;
         public renderingGroupId = 0;
         public layerMask: number = 0x0FFFFFFF;
         public layerMask: number = 0x0FFFFFFF;
@@ -43,6 +85,9 @@
         }
         }
 
 
         constructor(public name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon: number = 0.01, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
         constructor(public name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon: number = 0.01, samplingMode: number = Texture.TRILINEAR_SAMPLINGMODE) {
+            if (!scene._getComponent(SceneComponentConstants.NAME_SPRITE)) {
+                scene._addComponent(new SpriteSceneComponent(scene));
+            }
             this._capacity = capacity;
             this._capacity = capacity;
             this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
             this._spriteTexture = new Texture(imgUrl, scene, true, false, samplingMode);
             this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;
             this._spriteTexture.wrapU = Texture.CLAMP_ADDRESSMODE;

+ 297 - 0
src/Sprites/babylon.spriteSceneComponent.ts

@@ -0,0 +1,297 @@
+module BABYLON {
+    export interface Scene {
+        /** @hidden */
+        _pointerOverSprite: Nullable<Sprite>;
+
+        /** @hidden */
+        _pickedDownSprite: Nullable<Sprite>;
+
+        /** @hidden */
+        _tempSpritePickingRay: Nullable<Ray>;
+
+        /**
+         * All of the sprite managers added to this scene
+         * @see http://doc.babylonjs.com/babylon101/sprites
+         */
+        spriteManagers: Array<ISpriteManager>;
+
+        /**
+         * An event triggered when sprites rendering is about to start
+         * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
+         */
+        onBeforeSpritesRenderingObservable: Observable<Scene>;
+
+        /**
+         * An event triggered when sprites rendering is done
+         * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
+         */
+        onAfterSpritesRenderingObservable: Observable<Scene>;
+
+        /** @hidden */
+        _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** Launch a ray to try to pick a sprite in the scene
+         * @param x position on screen
+         * @param y position on screen
+         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
+         * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
+         * @returns a PickingInfo
+         */
+        pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** Use the given ray to pick a sprite in the scene
+         * @param ray The ray (in world space) to use to pick meshes
+         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
+         * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
+         * @returns a PickingInfo
+         */
+        pickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo>;
+
+        /** 
+         * Force the sprite under the pointer
+         * @param sprite defines the sprite to use
+         */
+        setPointerOverSprite(sprite: Nullable<Sprite>): void;
+
+        /** 
+         * Gets the sprite under the pointer
+         * @returns a Sprite or null if no sprite is under the pointer
+         */
+        getPointerOverSprite(): Nullable<Sprite>;
+    }
+
+    Scene.prototype._internalPickSprites = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        if (!PickingInfo) {
+            return null;
+        }
+
+        var pickingInfo = null;
+
+        if (!camera) {
+            if (!this.activeCamera) {
+                return null;
+            }
+            camera = this.activeCamera;
+        }
+
+        if (this.spriteManagers.length > 0) {
+            for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
+                var spriteManager = this.spriteManagers[spriteIndex];
+
+                if (!spriteManager.isPickable) {
+                    continue;
+                }
+
+                var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
+                if (!result || !result.hit)
+                    continue;
+
+                if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance)
+                    continue;
+
+                pickingInfo = result;
+
+                if (fastCheck) {
+                    break;
+                }
+            }
+        }
+
+        return pickingInfo || new PickingInfo();
+    }
+
+    Scene.prototype.pickSprite = function(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        this.createPickingRayInCameraSpaceToRef(x, y, this._tempSpritePickingRay!, camera);
+
+        return this._internalPickSprites(this._tempSpritePickingRay!, predicate, fastCheck, camera);
+    }
+
+    Scene.prototype.pickSpriteWithRay = function(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+        if (!this._tempSpritePickingRay) {
+            return null;
+        }
+
+        if (!camera) {
+            if (!this.activeCamera) {
+                return null;
+            }
+            camera = this.activeCamera;
+        }
+
+        Ray.TransformToRef(ray, camera.getViewMatrix(), this._tempSpritePickingRay);
+
+        return this._internalPickSprites(this._tempSpritePickingRay, predicate, fastCheck, camera);
+    }
+
+    Scene.prototype.setPointerOverSprite = function(sprite: Nullable<Sprite>): void {
+        if (this._pointerOverSprite === sprite) {
+            return;
+        }
+
+        if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
+            this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
+        }
+
+        this._pointerOverSprite = sprite;
+        if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
+            this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
+        }
+    }
+
+    Scene.prototype.getPointerOverSprite = function(): Nullable<Sprite> {
+        return this._pointerOverSprite;
+    }
+
+    /**
+     * Defines the sprite scene component responsible to manage sprites
+     * in a given scene.
+     */
+    export class SpriteSceneComponent implements ISceneComponent {
+        /**
+         * The component name helpfull to identify the component in the list of scene components.
+         */
+        public readonly name = SceneComponentConstants.NAME_SPRITE;
+
+        /**
+         * The scene the component belongs to.
+         */
+        public scene: Scene;
+
+        /** @hidden */
+        private _spritePredicate: (sprite: Sprite) => boolean;
+
+        /**
+         * Creates a new instance of the component for the given scene
+         * @param scene Defines the scene to register the component in
+         */
+        constructor(scene: Scene) {
+            this.scene = scene;
+            this.scene.spriteManagers = new Array<ISpriteManager>();
+            this.scene._tempSpritePickingRay = Ray ? Ray.Zero() : null;
+            this.scene.onBeforeSpritesRenderingObservable = new Observable<Scene>();
+            this.scene.onAfterSpritesRenderingObservable = new Observable<Scene>();
+            this._spritePredicate = (sprite: Sprite): boolean => {
+                return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
+            };
+        }
+
+        /**
+         * Registers the component in a given scene
+         */
+        public register(): void {
+            this.scene._pointerMoveStage.registerStep(SceneComponentConstants.STEP_POINTERMOVE_SPRITE, this, this._pointerMove);
+            this.scene._pointerDownStage.registerStep(SceneComponentConstants.STEP_POINTERDOWN_SPRITE, this, this._pointerDown);
+            this.scene._pointerUpStage.registerStep(SceneComponentConstants.STEP_POINTERUP_SPRITE, this, this._pointerUp);
+        }
+
+        /**
+         * Rebuilds the elements related to this component in case of
+         * context lost for instance.
+         */
+        public rebuild(): void {
+            /** Nothing to do for sprites */
+        }
+
+        /**
+         * Disposes the component and the associated ressources.
+         */
+        public dispose(): void {
+            this.scene.onBeforeSpritesRenderingObservable.clear();
+            this.scene.onAfterSpritesRenderingObservable.clear();
+
+            let spriteManagers = this.scene.spriteManagers;
+            while (spriteManagers.length) {
+                spriteManagers[0].dispose();
+            }
+        }
+
+        private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
+            var result = this.scene.pickSprite(x, y, this._spritePredicate, fastCheck, camera);
+            if (result) {
+                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
+            }
+            return result;
+        }
+
+        private _pointerMove(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, canvas: HTMLCanvasElement): Nullable<PickingInfo> {
+            var scene = this.scene;
+            if (isMeshPicked) {
+                scene.setPointerOverSprite(null);
+            } else {
+                pickResult = this._pickSpriteButKeepRay(pickResult, unTranslatedPointerX, unTranslatedPointerY, false, scene.cameraToUseForPointers || undefined);
+
+                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
+                    scene.setPointerOverSprite(pickResult.pickedSprite);
+                    if (scene._pointerOverSprite && scene._pointerOverSprite.actionManager && scene._pointerOverSprite.actionManager.hoverCursor) {
+                        canvas.style.cursor = scene._pointerOverSprite.actionManager.hoverCursor;
+                    } else {
+                        canvas.style.cursor = scene.hoverCursor;
+                    }
+                } else {
+                    scene.setPointerOverSprite(null);
+                    // Restore pointer
+                    canvas.style.cursor = scene.defaultCursor;
+                }
+            }
+
+            return pickResult;
+        }
+
+        private _pointerDown(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): Nullable<PickingInfo> {
+            var scene = this.scene;
+            scene._pickedDownSprite = null;
+            if (scene.spriteManagers.length > 0) {
+                pickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
+
+                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
+                    if (pickResult.pickedSprite.actionManager) {
+                        scene._pickedDownSprite = pickResult.pickedSprite;
+                        switch (evt.button) {
+                            case 0:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                            case 1:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                            case 2:
+                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                                break;
+                        }
+                        if (pickResult.pickedSprite.actionManager) {
+                            pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, scene, evt));
+                        }
+                    }
+                }
+            }
+
+            return pickResult;
+        }
+
+        private _pointerUp(unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent): Nullable<PickingInfo> {
+            var scene = this.scene;
+            if (scene.spriteManagers.length > 0) {
+                let spritePickResult = scene.pickSprite(unTranslatedPointerX, unTranslatedPointerY, this._spritePredicate, false, scene.cameraToUseForPointers || undefined);
+
+                if (spritePickResult) {
+                    if (spritePickResult.hit && spritePickResult.pickedSprite) {
+                        if (spritePickResult.pickedSprite.actionManager) {
+                            spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
+                            if (spritePickResult.pickedSprite.actionManager) {
+                                if (!this.scene._isPointerSwiping()) {
+                                    spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, scene, evt));
+                                }
+                            }
+                        }
+                    }
+                    if (scene._pickedDownSprite && scene._pickedDownSprite.actionManager && scene._pickedDownSprite !== spritePickResult.pickedSprite) {
+                        scene._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(scene._pickedDownSprite, scene, evt));
+                    }
+                }
+            }
+
+            return pickResult;
+        }
+    }
+}

+ 61 - 244
src/babylon.scene.ts

@@ -278,10 +278,6 @@
          */
          */
         public disableOfflineSupportExceptionRules = new Array<RegExp>();
         public disableOfflineSupportExceptionRules = new Array<RegExp>();
 
 
-        // Events
-
-        private _spritePredicate: (sprite: Sprite) => boolean;
-
         /**
         /**
         * An event triggered when the scene is disposed.
         * An event triggered when the scene is disposed.
         */
         */
@@ -416,18 +412,6 @@
         public onAfterParticlesRenderingObservable = new Observable<Scene>();
         public onAfterParticlesRenderingObservable = new Observable<Scene>();
 
 
         /**
         /**
-        * An event triggered when sprites rendering is about to start
-        * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
-        */
-        public onBeforeSpritesRenderingObservable = new Observable<Scene>();
-
-        /**
-        * An event triggered when sprites rendering is done
-        * Note: This event can be trigger more than once per frame (because sprites can be rendered by render target textures as well)
-        */
-        public onAfterSpritesRenderingObservable = new Observable<Scene>();
-
-        /**
         * An event triggered when SceneLoader.Append or SceneLoader.Load or SceneLoader.ImportMesh were successfully executed
         * An event triggered when SceneLoader.Append or SceneLoader.Load or SceneLoader.ImportMesh were successfully executed
         */
         */
         public onDataLoadedObservable = new Observable<Scene>();
         public onDataLoadedObservable = new Observable<Scene>();
@@ -805,11 +789,6 @@
         * Gets or sets a boolean indicating if sprites are enabled on this scene
         * Gets or sets a boolean indicating if sprites are enabled on this scene
         */
         */
         public spritesEnabled = true;
         public spritesEnabled = true;
-        /**
-        * All of the sprite managers added to this scene
-        * @see http://doc.babylonjs.com/babylon101/sprites
-        */
-        public spriteManagers = new Array<SpriteManager>();
 
 
         // Skeletons
         // Skeletons
         private _skeletonsEnabled = true;
         private _skeletonsEnabled = true;
@@ -1021,7 +1000,6 @@
         private _alternateSceneUbo: UniformBuffer;
         private _alternateSceneUbo: UniformBuffer;
 
 
         private _pickWithRayInverseMatrix: Matrix;
         private _pickWithRayInverseMatrix: Matrix;
-        private _outlineRenderer: OutlineRenderer;
 
 
         private _viewMatrix: Matrix;
         private _viewMatrix: Matrix;
         private _projectionMatrix: Matrix;
         private _projectionMatrix: Matrix;
@@ -1055,13 +1033,11 @@
         private _selectionOctree: Octree<AbstractMesh>;
         private _selectionOctree: Octree<AbstractMesh>;
 
 
         private _pointerOverMesh: Nullable<AbstractMesh>;
         private _pointerOverMesh: Nullable<AbstractMesh>;
-        private _pointerOverSprite: Nullable<Sprite>;
 
 
         private _debugLayer: DebugLayer;
         private _debugLayer: DebugLayer;
 
 
         private _pickedDownMesh: Nullable<AbstractMesh>;
         private _pickedDownMesh: Nullable<AbstractMesh>;
         private _pickedUpMesh: Nullable<AbstractMesh>;
         private _pickedUpMesh: Nullable<AbstractMesh>;
-        private _pickedDownSprite: Nullable<Sprite>;
         private _externalData: StringDictionary<Object>;
         private _externalData: StringDictionary<Object>;
         private _uid: Nullable<string>;
         private _uid: Nullable<string>;
 
 
@@ -1169,11 +1145,22 @@
          */
          */
         public _beforeCameraDrawStage = Stage.Create<CameraStageAction>();
         public _beforeCameraDrawStage = Stage.Create<CameraStageAction>();
         /**
         /**
+         * @hidden
          * Defines the actions happening just before a rendering group is drawing.
          * Defines the actions happening just before a rendering group is drawing.
          */
          */
         public _beforeRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
         public _beforeRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
         /**
         /**
          * @hidden
          * @hidden
+         * Defines the actions happening just before a mesh is drawing.
+         */
+        public _beforeRenderingMeshStage = Stage.Create<RenderingMeshStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening just after a mesh has been drawn.
+         */
+        public _afterRenderingMeshStage = Stage.Create<RenderingMeshStageAction>();
+        /**
+         * @hidden
          * Defines the actions happening just after a rendering group has been drawn.
          * Defines the actions happening just after a rendering group has been drawn.
          */
          */
         public _afterRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
         public _afterRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
@@ -1187,6 +1174,21 @@
          * Defines the actions happening when Geometries are rebuilding.
          * Defines the actions happening when Geometries are rebuilding.
          */
          */
         public _rebuildGeometryStage = Stage.Create<SimpleStageAction>();
         public _rebuildGeometryStage = Stage.Create<SimpleStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer move event happens.
+         */
+        public _pointerMoveStage = Stage.Create<PointerMoveStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer down event happens.
+         */
+        public _pointerDownStage = Stage.Create<PointerUpDownStageAction>();
+        /**
+         * @hidden
+         * Defines the actions happening when a pointer up event happens.
+         */
+        public _pointerUpStage = Stage.Create<PointerUpDownStageAction>();
 
 
         /**
         /**
          * Creates a new Scene
          * Creates a new Scene
@@ -1205,10 +1207,6 @@
                 this.postProcessManager = new PostProcessManager(this);
                 this.postProcessManager = new PostProcessManager(this);
             }
             }
 
 
-            if (OutlineRenderer) {
-                this._outlineRenderer = new OutlineRenderer(this);
-            }
-
             if (Tools.IsWindowObjectExist()) {
             if (Tools.IsWindowObjectExist()) {
                 this.attachControl();
                 this.attachControl();
             }
             }
@@ -1326,14 +1324,6 @@
         }
         }
 
 
         /** 
         /** 
-         * Gets the outline renderer associated with the scene
-         * @returns a OutlineRenderer
-         */
-        public getOutlineRenderer(): OutlineRenderer {
-            return this._outlineRenderer;
-        }
-
-        /** 
          * Gets the engine associated with the scene
          * Gets the engine associated with the scene
          * @returns an Engine
          * @returns an Engine
          */
          */
@@ -1539,14 +1529,6 @@
         }
         }
 
 
         // Pointers handling
         // Pointers handling
-        private _pickSpriteButKeepRay(originalPointerInfo: Nullable<PickingInfo>, x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            var result = this.pickSprite(x, y, predicate, fastCheck, camera);
-            if (result) {
-                result.ray = originalPointerInfo ? originalPointerInfo.ray : null;
-            }
-            return result;
-        }
-
         private _setRayOnPointerInfo(pointerInfo: PointerInfo) {
         private _setRayOnPointerInfo(pointerInfo: PointerInfo) {
             if (pointerInfo.pickInfo) {
             if (pointerInfo.pickInfo) {
                 if (!pointerInfo.pickInfo.ray) {
                 if (!pointerInfo.pickInfo.ray) {
@@ -1579,37 +1561,15 @@
                 return this;
                 return this;
             }
             }
 
 
-            if (pickResult && pickResult.hit && pickResult.pickedMesh) {
-                this.setPointerOverSprite(null);
-
-                this.setPointerOverMesh(pickResult.pickedMesh);
-
-                if (this._pointerOverMesh && this._pointerOverMesh.actionManager && this._pointerOverMesh.actionManager.hasPointerTriggers) {
-                    if (this._pointerOverMesh.actionManager.hoverCursor) {
-                        canvas.style.cursor = this._pointerOverMesh.actionManager.hoverCursor;
-                    } else {
-                        canvas.style.cursor = this.hoverCursor;
-                    }
-                } else {
-                    canvas.style.cursor = this.defaultCursor;
-                }
+            var isMeshPicked = (pickResult && pickResult.hit && pickResult.pickedMesh) ? true : false;
+            if (isMeshPicked) {
+                this.setPointerOverMesh(pickResult!.pickedMesh);
             } else {
             } else {
                 this.setPointerOverMesh(null);
                 this.setPointerOverMesh(null);
-                // Sprites
-                pickResult = this._pickSpriteButKeepRay(pickResult, this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
+            }
 
 
-                if (pickResult && pickResult.hit && pickResult.pickedSprite) {
-                    this.setPointerOverSprite(pickResult.pickedSprite);
-                    if (this._pointerOverSprite && this._pointerOverSprite.actionManager && this._pointerOverSprite.actionManager.hoverCursor) {
-                        canvas.style.cursor = this._pointerOverSprite.actionManager.hoverCursor;
-                    } else {
-                        canvas.style.cursor = this.hoverCursor;
-                    }
-                } else {
-                    this.setPointerOverSprite(null);
-                    // Restore pointer
-                    canvas.style.cursor = this.defaultCursor;
-                }
+            for (let step of this._pointerMoveStage) {
+                pickResult = step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, isMeshPicked, canvas);
             }
             }
 
 
             if (pickResult) {
             if (pickResult) {
@@ -1688,8 +1648,7 @@
                             if (pickResult && pickResult.hit && pickResult.pickedMesh && actionManager) {
                             if (pickResult && pickResult.hit && pickResult.pickedMesh && actionManager) {
                                 if (this._totalPointersPressed !== 0 &&
                                 if (this._totalPointersPressed !== 0 &&
                                     ((Date.now() - this._startingPointerTime) > Scene.LongPressDelay) &&
                                     ((Date.now() - this._startingPointerTime) > Scene.LongPressDelay) &&
-                                    (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold &&
-                                        Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold)) {
+                                    !this._isPointerSwiping()) {
                                     this._startingPointerTime = 0;
                                     this._startingPointerTime = 0;
                                     actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                     actionManager.processTrigger(ActionManager.OnLongPressTrigger, ActionEvent.CreateNew(pickResult.pickedMesh, evt));
                                 }
                                 }
@@ -1698,6 +1657,11 @@
                     }
                     }
                 }
                 }
             }
             }
+            else {
+                for (let step of this._pointerDownStage) {
+                    pickResult = step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, evt);
+                }
+            }
 
 
             if (pickResult) {
             if (pickResult) {
                 let type = PointerEventTypes.POINTERDOWN;
                 let type = PointerEventTypes.POINTERDOWN;
@@ -1762,6 +1726,14 @@
                     }
                     }
                 }
                 }
             }
             }
+            else {
+                if (!clickInfo.ignore) {
+                    for (let step of this._pointerUpStage) {
+                        pickResult = step.action(this._unTranslatedPointerX, this._unTranslatedPointerY, pickResult, evt);
+                    }
+                }
+            }
+
             if (this._pickedDownMesh &&
             if (this._pickedDownMesh &&
                 this._pickedDownMesh.actionManager &&
                 this._pickedDownMesh.actionManager &&
                 this._pickedDownMesh.actionManager.hasSpecificTrigger(ActionManager.OnPickOutTrigger) &&
                 this._pickedDownMesh.actionManager.hasSpecificTrigger(ActionManager.OnPickOutTrigger) &&
@@ -1810,6 +1782,12 @@
             return this._pointerCaptures[pointerId];
             return this._pointerCaptures[pointerId];
         }
         }
 
 
+        /** @hidden */
+        public _isPointerSwiping(): boolean {
+            return Math.abs(this._startingPointerPosition.x - this._pointerX) > Scene.DragMovementThreshold ||
+                   Math.abs(this._startingPointerPosition.y - this._pointerY) > Scene.DragMovementThreshold;
+        }
+
         /**
         /**
         * Attach events to the canvas (To handle actionManagers triggers and raise onPointerMove, onPointerDown and onPointerUp
         * Attach events to the canvas (To handle actionManagers triggers and raise onPointerMove, onPointerDown and onPointerUp
         * @param attachUp defines if you want to attach events to pointerup
         * @param attachUp defines if you want to attach events to pointerup
@@ -1855,8 +1833,7 @@
                 }
                 }
                 if (checkPicking) {
                 if (checkPicking) {
                     let btn = evt.button;
                     let btn = evt.button;
-                    clickInfo.hasSwiped = Math.abs(this._startingPointerPosition.x - this._pointerX) > Scene.DragMovementThreshold ||
-                        Math.abs(this._startingPointerPosition.y - this._pointerY) > Scene.DragMovementThreshold;
+                    clickInfo.hasSwiped = this._isPointerSwiping();
 
 
                     if (!clickInfo.hasSwiped) {
                     if (!clickInfo.hasSwiped) {
                         let checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
                         let checkSingleClickImmediately = !Scene.ExclusiveDoubleClickMode;
@@ -1903,8 +1880,7 @@
                             ) {
                             ) {
                                 // pointer has not moved for 2 clicks, it's a double click
                                 // pointer has not moved for 2 clicks, it's a double click
                                 if (!clickInfo.hasSwiped &&
                                 if (!clickInfo.hasSwiped &&
-                                    Math.abs(this._previousStartingPointerPosition.x - this._startingPointerPosition.x) < Scene.DragMovementThreshold &&
-                                    Math.abs(this._previousStartingPointerPosition.y - this._startingPointerPosition.y) < Scene.DragMovementThreshold) {
+                                    !this._isPointerSwiping()) {
                                     this._previousStartingPointerTime = 0;
                                     this._previousStartingPointerTime = 0;
                                     this._doubleClickOccured = true;
                                     this._doubleClickOccured = true;
                                     clickInfo.doubleClick = true;
                                     clickInfo.doubleClick = true;
@@ -1950,10 +1926,6 @@
                 cb(clickInfo, this._currentPickResult);
                 cb(clickInfo, this._currentPickResult);
             };
             };
 
 
-            this._spritePredicate = (sprite: Sprite): boolean => {
-                return sprite.isPickable && sprite.actionManager && sprite.actionManager.hasPointerTriggers;
-            };
-
             this._onPointerMove = (evt: PointerEvent) => {
             this._onPointerMove = (evt: PointerEvent) => {
 
 
                 this._updatePointerPosition(evt);
                 this._updatePointerPosition(evt);
@@ -2014,32 +1986,6 @@
                 var pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY, this.pointerDownPredicate, false, this.cameraToUseForPointers);
                 var pickResult = this.pick(this._unTranslatedPointerX, this._unTranslatedPointerY, this.pointerDownPredicate, false, this.cameraToUseForPointers);
 
 
                 this._processPointerDown(pickResult, evt);
                 this._processPointerDown(pickResult, evt);
-
-                // Sprites
-                this._pickedDownSprite = null;
-                if (this.spriteManagers.length > 0) {
-                    pickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
-
-                    if (pickResult && pickResult.hit && pickResult.pickedSprite) {
-                        if (pickResult.pickedSprite.actionManager) {
-                            this._pickedDownSprite = pickResult.pickedSprite;
-                            switch (evt.button) {
-                                case 0:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnLeftPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                                case 1:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnCenterPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                                case 2:
-                                    pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnRightPickTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                                    break;
-                            }
-                            if (pickResult.pickedSprite.actionManager) {
-                                pickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickDownTrigger, ActionEvent.CreateNewFromSprite(pickResult.pickedSprite, this, evt));
-                            }
-                        }
-                    }
-                }
             };
             };
 
 
             this._onPointerUp = (evt: PointerEvent) => {
             this._onPointerUp = (evt: PointerEvent) => {
@@ -2098,29 +2044,6 @@
 
 
                     this._processPointerUp(pickResult, evt, clickInfo);
                     this._processPointerUp(pickResult, evt, clickInfo);
 
 
-                    // Sprites
-                    if (!clickInfo.ignore) {
-                        if (this.spriteManagers.length > 0) {
-                            let spritePickResult = this.pickSprite(this._unTranslatedPointerX, this._unTranslatedPointerY, this._spritePredicate, false, this.cameraToUseForPointers || undefined);
-
-                            if (spritePickResult) {
-                                if (spritePickResult.hit && spritePickResult.pickedSprite) {
-                                    if (spritePickResult.pickedSprite.actionManager) {
-                                        spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickUpTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, this, evt));
-                                        if (spritePickResult.pickedSprite.actionManager) {
-                                            if (Math.abs(this._startingPointerPosition.x - this._pointerX) < Scene.DragMovementThreshold && Math.abs(this._startingPointerPosition.y - this._pointerY) < Scene.DragMovementThreshold) {
-                                                spritePickResult.pickedSprite.actionManager.processTrigger(ActionManager.OnPickTrigger, ActionEvent.CreateNewFromSprite(spritePickResult.pickedSprite, this, evt));
-                                            }
-                                        }
-                                    }
-                                }
-                                if (this._pickedDownSprite && this._pickedDownSprite.actionManager && this._pickedDownSprite !== spritePickResult.pickedSprite) {
-                                    this._pickedDownSprite.actionManager.processTrigger(ActionManager.OnPickOutTrigger, ActionEvent.CreateNewFromSprite(this._pickedDownSprite, this, evt));
-                                }
-                            }
-                        }
-                    }
-
                     this._previousPickResult = this._currentPickResult;
                     this._previousPickResult = this._currentPickResult;
                 });
                 });
             };
             };
@@ -4881,11 +4804,17 @@
             this._cameraDrawRenderTargetStage.clear();
             this._cameraDrawRenderTargetStage.clear();
             this._beforeCameraDrawStage.clear();
             this._beforeCameraDrawStage.clear();
             this._beforeRenderingGroupDrawStage.clear();
             this._beforeRenderingGroupDrawStage.clear();
+            this._beforeRenderingMeshStage.clear();
+            this._afterRenderingMeshStage.clear();
             this._afterRenderingGroupDrawStage.clear();
             this._afterRenderingGroupDrawStage.clear();
             this._afterCameraDrawStage.clear();
             this._afterCameraDrawStage.clear();
             this._beforeCameraUpdateStage.clear();
             this._beforeCameraUpdateStage.clear();
             this._gatherRenderTargetsStage.clear();
             this._gatherRenderTargetsStage.clear();
             this._rebuildGeometryStage.clear();
             this._rebuildGeometryStage.clear();
+            this._pointerMoveStage.clear();
+            this._pointerDownStage.clear();
+            this._pointerUpStage.clear();
+
             for (let component of this._components) {
             for (let component of this._components) {
                 component.dispose();
                 component.dispose();
             }
             }
@@ -4936,8 +4865,6 @@
             this.onAfterActiveMeshesEvaluationObservable.clear();
             this.onAfterActiveMeshesEvaluationObservable.clear();
             this.onBeforeParticlesRenderingObservable.clear();
             this.onBeforeParticlesRenderingObservable.clear();
             this.onAfterParticlesRenderingObservable.clear();
             this.onAfterParticlesRenderingObservable.clear();
-            this.onBeforeSpritesRenderingObservable.clear();
-            this.onAfterSpritesRenderingObservable.clear();
             this.onBeforeDrawPhaseObservable.clear();
             this.onBeforeDrawPhaseObservable.clear();
             this.onAfterDrawPhaseObservable.clear();
             this.onAfterDrawPhaseObservable.clear();
             this.onBeforePhysicsObservable.clear();
             this.onBeforePhysicsObservable.clear();
@@ -5009,11 +4936,6 @@
                 this.particleSystems[0].dispose();
                 this.particleSystems[0].dispose();
             }
             }
 
 
-            // Release sprites
-            while (this.spriteManagers.length) {
-                this.spriteManagers[0].dispose();
-            }
-
             // Release postProcesses
             // Release postProcesses
             while (this.postProcesses.length) {
             while (this.postProcesses.length) {
                 this.postProcesses[0].dispose();
                 this.postProcesses[0].dispose();
@@ -5329,46 +5251,6 @@
             return pickingInfos;
             return pickingInfos;
         }
         }
 
 
-        private _internalPickSprites(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            if (!PickingInfo) {
-                return null;
-            }
-
-            var pickingInfo = null;
-
-            if (!camera) {
-                if (!this.activeCamera) {
-                    return null;
-                }
-                camera = this.activeCamera;
-            }
-
-            if (this.spriteManagers.length > 0) {
-                for (var spriteIndex = 0; spriteIndex < this.spriteManagers.length; spriteIndex++) {
-                    var spriteManager = this.spriteManagers[spriteIndex];
-
-                    if (!spriteManager.isPickable) {
-                        continue;
-                    }
-
-                    var result = spriteManager.intersects(ray, camera, predicate, fastCheck);
-                    if (!result || !result.hit)
-                        continue;
-
-                    if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance)
-                        continue;
-
-                    pickingInfo = result;
-
-                    if (fastCheck) {
-                        break;
-                    }
-                }
-            }
-
-            return pickingInfo || new PickingInfo();
-        }
-
         private _tempPickingRay: Nullable<Ray> = Ray ? Ray.Zero() : null;
         private _tempPickingRay: Nullable<Ray> = Ray ? Ray.Zero() : null;
 
 
         /** Launch a ray to try to pick a mesh in the scene
         /** Launch a ray to try to pick a mesh in the scene
@@ -5393,49 +5275,11 @@
             return result;
             return result;
         }
         }
 
 
-        /** Launch a ray to try to pick a sprite in the scene
-         * @param x position on screen
-         * @param y position on screen
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
-         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
-         * @param camera camera to use for computing the picking ray. Can be set to null. In this case, the scene.activeCamera will be used
-         * @returns a PickingInfo
-         */
-        public pickSprite(x: number, y: number, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            this.createPickingRayInCameraSpaceToRef(x, y, this._tempPickingRay!, camera);
-
-            return this._internalPickSprites(this._tempPickingRay!, predicate, fastCheck, camera);
-        }
-
         private _cachedRayForTransform: Ray;
         private _cachedRayForTransform: Ray;
 
 
-        /** Use the given ray to pick a sprite in the scene
-         * @param ray The ray (in world space) to use to pick meshes
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
-         * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null.
-         * @param camera camera to use. Can be set to null. In this case, the scene.activeCamera will be used
-         * @returns a PickingInfo
-         */
-        public pickSpriteWithRay(ray: Ray, predicate?: (sprite: Sprite) => boolean, fastCheck?: boolean, camera?: Camera): Nullable<PickingInfo> {
-            if (!this._cachedRayForTransform) {
-                this._cachedRayForTransform = Ray.Zero();
-            }
-
-            if (!camera) {
-                if (!this.activeCamera) {
-                    return null;
-                }
-                camera = this.activeCamera;
-            }
-
-            Ray.TransformToRef(ray, camera.getViewMatrix(), this._cachedRayForTransform);
-
-            return this._internalPickSprites(this._cachedRayForTransform, predicate, fastCheck, camera);
-        }       
-
         /** Use the given ray to pick a mesh in the scene
         /** Use the given ray to pick a mesh in the scene
          * @param ray The ray to use to pick meshes
          * @param ray The ray to use to pick meshes
-         * @param predicate Predicate function used to determine eligible sprites. Can be set to null. In this case, a sprite must have isPickable set to true
+         * @param predicate Predicate function used to determine eligible meshes. Can be set to null. In this case, a mesh must have isPickable set to true
          * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null
          * @param fastCheck Launch a fast check only using the bounding boxes. Can be set to null
          * @returns a PickingInfo
          * @returns a PickingInfo
          */
          */
@@ -5520,33 +5364,6 @@
             return this._pointerOverMesh;
             return this._pointerOverMesh;
         }
         }
 
 
-        /** 
-         * Force the sprite under the pointer
-         * @param sprite defines the sprite to use
-         */
-        public setPointerOverSprite(sprite: Nullable<Sprite>): void {
-            if (this._pointerOverSprite === sprite) {
-                return;
-            }
-
-            if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
-                this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOutTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
-            }
-
-            this._pointerOverSprite = sprite;
-            if (this._pointerOverSprite && this._pointerOverSprite.actionManager) {
-                this._pointerOverSprite.actionManager.processTrigger(ActionManager.OnPointerOverTrigger, ActionEvent.CreateNewFromSprite(this._pointerOverSprite, this));
-            }
-        }
-
-        /** 
-         * Gets the sprite under the pointer
-         * @returns a Sprite or null if no sprite is under the pointer
-         */
-        public getPointerOverSprite(): Nullable<Sprite> {
-            return this._pointerOverSprite;
-        }
-
         // Physics
         // Physics
 
 
         /** 
         /** 

+ 25 - 0
src/babylon.sceneComponent.ts

@@ -14,6 +14,8 @@
         public static readonly NAME_GEOMETRYBUFFERRENDERER = "GeometryBufferRenderer";
         public static readonly NAME_GEOMETRYBUFFERRENDERER = "GeometryBufferRenderer";
         public static readonly NAME_DEPTHRENDERER = "DepthRenderer";
         public static readonly NAME_DEPTHRENDERER = "DepthRenderer";
         public static readonly NAME_POSTPROCESSRENDERPIPELINEMANAGER = "PostProcessRenderPipelineManager";
         public static readonly NAME_POSTPROCESSRENDERPIPELINEMANAGER = "PostProcessRenderPipelineManager";
+        public static readonly NAME_SPRITE = "Sprite";
+        public static readonly NAME_OUTLINERENDERER = "Outline";
 
 
         public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;
         public static readonly STEP_ISREADYFORMESH_EFFECTLAYER = 0;
 
 
@@ -28,6 +30,10 @@
         public static readonly STEP_BEFORECAMERADRAW_EFFECTLAYER = 0;
         public static readonly STEP_BEFORECAMERADRAW_EFFECTLAYER = 0;
         public static readonly STEP_BEFORECAMERADRAW_LAYER = 1;
         public static readonly STEP_BEFORECAMERADRAW_LAYER = 1;
 
 
+        public static readonly STEP_BEFORERENDERINGMESH_OUTLINE = 0;
+
+        public static readonly STEP_AFTERRENDERINGMESH_OUTLINE = 0;
+
         public static readonly STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW = 0;
         public static readonly STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW = 0;
 
 
         public static readonly STEP_BEFORECAMERAUPDATE_SIMPLIFICATIONQUEUE = 0;
         public static readonly STEP_BEFORECAMERAUPDATE_SIMPLIFICATIONQUEUE = 0;
@@ -44,6 +50,10 @@
         public static readonly STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER = 2;
         public static readonly STEP_GATHERRENDERTARGETS_POSTPROCESSRENDERPIPELINEMANAGER = 2;
 
 
         public static readonly STEP_REBUILDGEOMETRY_POSTPROCESSRENDERPIPELINEMANAGER = 0;
         public static readonly STEP_REBUILDGEOMETRY_POSTPROCESSRENDERPIPELINEMANAGER = 0;
+
+        public static readonly STEP_POINTERMOVE_SPRITE = 0;
+        public static readonly STEP_POINTERDOWN_SPRITE = 0;
+        public static readonly STEP_POINTERUP_SPRITE = 0;
     }
     }
 
 
     /**
     /**
@@ -131,6 +141,11 @@
     export type RenderingGroupStageAction = (renderingGroupId: number) => void;
     export type RenderingGroupStageAction = (renderingGroupId: number) => void;
 
 
     /** 
     /** 
+     * Strong typing of a Mesh Render related stage step action 
+     */
+    export type RenderingMeshStageAction = (mesh: AbstractMesh, subMesh: SubMesh, batch: _InstancesBatch) => void;
+
+    /** 
      * Strong typing of a simple stage step action 
      * Strong typing of a simple stage step action 
      */
      */
     export type SimpleStageAction = () => void;
     export type SimpleStageAction = () => void;
@@ -141,6 +156,16 @@
     export type RenderTargetsStageAction = (renderTargets: SmartArrayNoDuplicate<RenderTargetTexture>) => void;
     export type RenderTargetsStageAction = (renderTargets: SmartArrayNoDuplicate<RenderTargetTexture>) => void;
 
 
     /** 
     /** 
+     * Strong typing of a pointer move action.
+     */
+    export type PointerMoveStageAction = (unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, isMeshPicked: boolean, canvas: HTMLCanvasElement) => Nullable<PickingInfo>;
+
+    /** 
+     * Strong typing of a pointer up/down action.
+     */
+    export type PointerUpDownStageAction = (unTranslatedPointerX: number, unTranslatedPointerY: number, pickResult: Nullable<PickingInfo>, evt: PointerEvent) => Nullable<PickingInfo>;
+
+    /** 
      * Repressentation of a stage in the scene (Basically a list of ordered steps) 
      * Repressentation of a stage in the scene (Basically a list of ordered steps) 
      * @hidden
      * @hidden
      */
      */

BIN
tests/validation/ReferenceImages/edges.png


BIN
tests/validation/ReferenceImages/outline.png


+ 14 - 4
tests/validation/config.json

@@ -1,16 +1,16 @@
 {
 {
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "root": "https://rawgit.com/BabylonJS/Website/master",
-  "tests": [  
+  "tests": [
     {
     {
       "title": "Clip planes",
       "title": "Clip planes",
       "playgroundId": "#Y6W087#0",
       "playgroundId": "#Y6W087#0",
       "referenceImage": "clipplanes.png"
       "referenceImage": "clipplanes.png"
-    },         
+    },
     {
     {
       "title": "ShadowOnlyMaterial",
       "title": "ShadowOnlyMaterial",
       "playgroundId": "#1KF7V1#18",
       "playgroundId": "#1KF7V1#18",
       "referenceImage": "shadowOnlyMaterial.png"
       "referenceImage": "shadowOnlyMaterial.png"
-    },     
+    },
     {
     {
       "title": "Gizmos",
       "title": "Gizmos",
       "playgroundId": "#8GY6J8#48",
       "playgroundId": "#8GY6J8#48",
@@ -535,6 +535,16 @@
       "playgroundId": "#3HPMAA#0",
       "playgroundId": "#3HPMAA#0",
       "renderCount": 50,
       "renderCount": 50,
       "referenceImage": "depthRenderer.png"
       "referenceImage": "depthRenderer.png"
+    },
+    {
+      "title": "Edges",
+      "playgroundId": "#TYAHX#113",
+      "referenceImage": "edges.png"
+    },
+    {
+      "title": "Outline",
+      "playgroundId": "#10WJ5S#6",
+      "referenceImage": "outline.png"
     }
     }
   ]
   ]
-}
+}

+ 3 - 0
tests/validation/validation.js

@@ -48,6 +48,9 @@ function compare(renderData, referenceCanvas) {
 
 
     referenceContext.putImageData(referenceData, 0, 0);
     referenceContext.putImageData(referenceData, 0, 0);
 
 
+    if (differencesCount) {
+        console.log("Pixel difference: " + differencesCount + " pixels.")
+    }
     return (differencesCount * 100) / (width * height) > errorRatio;
     return (differencesCount * 100) / (width * height) > errorRatio;
 }
 }