Forráskód Böngészése

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

Jaskar 7 éve
szülő
commit
e437b5b877
56 módosított fájl, 13921 hozzáadás és 11930 törlés
  1. 8755 8673
      Playground/babylon.d.txt
  2. 30 28
      Tools/DevLoader/BabylonLoader.js
  3. 40 1
      Tools/Gulp/config.json
  4. 5 3
      Tools/Gulp/gulpfile.js
  5. 87 31
      Tools/Publisher/index.js
  6. 31 0
      Viewer/assets/package.json
  7. 19 0
      Viewer/assets/readme.md
  8. 1 1
      Viewer/src/index.ts
  9. 2 2
      Viewer/src/initializer.ts
  10. 0 1
      Viewer/src/loader/plugins/applyMaterialConfig.ts
  11. 0 1
      Viewer/src/loader/plugins/extendedMaterialLoaderPlugin.ts
  12. 0 1
      Viewer/src/loader/plugins/msftLodLoaderPlugin.ts
  13. 1 1
      Viewer/src/loader/plugins/telemetryLoaderPlugin.ts
  14. 0 0
      Viewer/src/managers/telemetryManager.ts
  15. 29 19
      Viewer/src/viewer/defaultViewer.ts
  16. 1 1
      Viewer/src/viewer/viewer.ts
  17. 1 1
      Viewer/tsconfig.json
  18. 42 0
      Viewer/webpack.assets.config.js
  19. 1 2
      Viewer/webpack.gulp.config.js
  20. 2749 2667
      dist/preview release/babylon.d.ts
  21. 49 49
      dist/preview release/babylon.js
  22. 169 25
      dist/preview release/babylon.max.js
  23. 169 25
      dist/preview release/babylon.no-module.max.js
  24. 31 31
      dist/preview release/babylon.worker.js
  25. 169 25
      dist/preview release/es6.js
  26. 83 0
      dist/preview release/gui/babylon.gui.d.ts
  27. 310 3
      dist/preview release/gui/babylon.gui.js
  28. 4 4
      dist/preview release/gui/babylon.gui.min.js
  29. 83 0
      dist/preview release/gui/babylon.gui.module.d.ts
  30. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  31. 30 28
      dist/preview release/inspector/babylon.inspector.js
  32. 2 2
      dist/preview release/inspector/babylon.inspector.min.js
  33. 1 1
      dist/preview release/viewer/babylon.viewer.d.ts
  34. 41 41
      dist/preview release/viewer/babylon.viewer.js
  35. 320 165
      dist/preview release/viewer/babylon.viewer.max.js
  36. 3 3
      dist/preview release/viewer/babylon.viewer.module.d.ts
  37. 11 0
      dist/preview release/viewer/package.json
  38. 6 2
      dist/preview release/what's new.md
  39. 3 3
      gui/src/2D/controls/container.ts
  40. 3 0
      gui/src/2D/controls/control.ts
  41. 341 0
      gui/src/2D/controls/grid.ts
  42. 1 1
      gui/src/3D/controls/control3D.ts
  43. 31 29
      inspector/src/tabs/ToolsTab.ts
  44. 102 1
      src/Helpers/babylon.particleHelper.ts
  45. 7 0
      src/Helpers/babylon.photoDome.ts
  46. 7 0
      src/Helpers/babylon.videoDome.ts
  47. 1 1
      src/Particles/EmitterTypes/babylon.IParticleEmitterType.ts
  48. 30 21
      src/Particles/EmitterTypes/babylon.coneParticleEmitter.ts
  49. 19 1
      src/Particles/babylon.IParticleSystem.ts
  50. 31 10
      src/Particles/babylon.gpuParticleSystem.ts
  51. 6 0
      src/Particles/babylon.particle.ts
  52. 49 14
      src/Particles/babylon.particleSystem.ts
  53. 1 1
      src/Particles/babylon.solidParticleSystem.ts
  54. 2 2
      src/Shaders/gpuRenderParticles.vertex.fx
  55. 7 4
      src/Shaders/gpuUpdateParticles.vertex.fx
  56. 3 3
      src/Shaders/particles.vertex.fx

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 8755 - 8673
Playground/babylon.d.txt


+ 30 - 28
Tools/DevLoader/BabylonLoader.js

@@ -2,8 +2,8 @@
 
 var BABYLONDEVTOOLS;
 (function (BABYLONDEVTOOLS) {
-    
-    var getJson = function(url, callback, errorCallback) {
+
+    var getJson = function (url, callback, errorCallback) {
         var xhr = new XMLHttpRequest();
         xhr.open('GET', url);
         xhr.onload = function () {
@@ -12,8 +12,8 @@ var BABYLONDEVTOOLS;
                 callback(data)
             } else {
                 errorCallback({
-                status: this.status,
-                statusText: xhr.statusText
+                    status: this.status,
+                    statusText: xhr.statusText
                 });
             }
         };
@@ -39,19 +39,19 @@ var BABYLONDEVTOOLS;
             dependencies = [];
             callback = null;
             min = (document.location.href.toLowerCase().indexOf('dist=min') > 0);
-            useDist = (min || useDist || document.location.href.toLowerCase().indexOf('dist=true') > 0);            
+            useDist = (min || useDist || document.location.href.toLowerCase().indexOf('dist=true') > 0);
             babylonJSPath = '';
         }
 
-        Loader.prototype.debugShortcut = function(engine) {
+        Loader.prototype.debugShortcut = function (engine) {
             // Add inspector shortcut
             var map = {};
-            var onkey = function(e){
+            var onkey = function (e) {
                 e = e || event; // to deal with IE
                 map[e.keyCode] = e.type == 'keydown';
-                if(map[17] && map[16] && map[18] && map[73]) {
+                if (map[17] && map[16] && map[18] && map[73]) {
                     if (engine.scenes && engine.scenes.length > 0) {
-                        for (var i = 0; i < engine.scenes.length; i ++) {
+                        for (var i = 0; i < engine.scenes.length; i++) {
                             if (engine.scenes[0].debugLayer.isVisible()) {
                                 engine.scenes[0].debugLayer.hide();
                             }
@@ -91,7 +91,7 @@ var BABYLONDEVTOOLS;
             return this;
         }
 
-        Loader.prototype.useDist = function() {
+        Loader.prototype.useDist = function () {
             useDist = true;
             return this;
         }
@@ -99,22 +99,22 @@ var BABYLONDEVTOOLS;
         Loader.prototype.dequeue = function () {
             if (queue.length == 0) {
                 console.log('Scripts loaded');
-                BABYLON.Engine.ShadersRepository = "/src/Shaders/"; 
-                if (callback) {                    
+                BABYLON.Engine.ShadersRepository = "/src/Shaders/";
+                if (callback) {
                     callback();
                 }
-                return;                
+                return;
             }
 
             var url = queue.shift();
-            
+
             var head = document.getElementsByTagName('head')[0];
             var script = document.createElement('script');
             script.type = 'text/javascript';
             script.src = url;
 
             var self = this;
-            script.onload = function() {
+            script.onload = function () {
                 self.dequeue();
             };
             head.appendChild(script);
@@ -135,7 +135,7 @@ var BABYLONDEVTOOLS;
         }
 
         Loader.prototype.loadScripts = function (urls) {
-            for (var i = 0; i< urls.length; i++) {
+            for (var i = 0; i < urls.length; i++) {
                 this.loadScript(urls[i]);
             }
         }
@@ -147,7 +147,7 @@ var BABYLONDEVTOOLS;
                     var file = library.files[i];
                     if (file.indexOf('lib.d.ts') > 0) {
                         continue;
-                    } 
+                    }
 
                     file = file.replace('.ts', '.js');
                     file = file.replace('../', '');
@@ -169,39 +169,41 @@ var BABYLONDEVTOOLS;
             }
             else if (min) {
                 if (library.webpack) {
-                    this.loadScript(babylonJSPath + '/dist/preview release' + module.build.distOutputDirectory + library.output.replace('.js', '.bundle.js'));
+                    if (module.build.distOutputDirectory)
+                        this.loadScript(babylonJSPath + '/dist/preview release' + module.build.distOutputDirectory + library.output.replace('.js', '.bundle.js'));
                 }
                 else {
                     this.loadScript(babylonJSPath + '/dist/preview release' + module.build.distOutputDirectory + library.output.replace('.js', '.min.js'));
                 }
             }
             else {
-                this.loadScript(babylonJSPath + '/dist/preview release' + module.build.distOutputDirectory + library.output);
+                if (module.build.distOutputDirectory)
+                    this.loadScript(babylonJSPath + '/dist/preview release' + module.build.distOutputDirectory + library.output);
             }
 
             if (!min && library.sassFiles && library.sassFiles.length > 0) {
                 var cssFile = library.output.replace('.js', '.css');
-                cssFile = babylonJSPath + '/dist/preview release' +  module.build.distOutputDirectory + cssFile;
+                cssFile = babylonJSPath + '/dist/preview release' + module.build.distOutputDirectory + cssFile;
                 this.loadCss(cssFile);
             }
         }
 
         Loader.prototype.loadModule = function (module) {
-            for (var i = 0; i< module.libraries.length; i++) {
+            for (var i = 0; i < module.libraries.length; i++) {
                 this.loadLibrary(module.libraries[i], module);
             }
         }
 
-        Loader.prototype.processDependency = function(settings, dependency, filesToLoad) {
+        Loader.prototype.processDependency = function (settings, dependency, filesToLoad) {
             if (dependency.dependUpon) {
-                for (var i = 0; i < dependency.dependUpon.length; i++ ) {
+                for (var i = 0; i < dependency.dependUpon.length; i++) {
                     var dependencyName = dependency.dependUpon[i];
                     var parent = settings.workloads[dependencyName];
                     this.processDependency(settings, parent, filesToLoad);
                 }
             }
 
-            for (var i = 0; i< dependency.files.length; i++) {
+            for (var i = 0; i < dependency.files.length; i++) {
                 var file = dependency.files[i];
 
                 if (filesToLoad.indexOf(file) === -1) {
@@ -240,7 +242,7 @@ var BABYLONDEVTOOLS;
 
             // Modules
             if (loadModules) {
-                for (var i = 0; i< settings.modules.length; i++) {
+                for (var i = 0; i < settings.modules.length; i++) {
                     if (settings.modules[i] === "viewer") {
                         continue;
                     }
@@ -255,7 +257,7 @@ var BABYLONDEVTOOLS;
                 callback = newCallback;
             }
             getJson('/Tools/Gulp/config.json',
-                function(data) {
+                function (data) {
                     if (!min) {
                         self.loadScript('/dist/preview release/split.js');
                     }
@@ -267,14 +269,14 @@ var BABYLONDEVTOOLS;
 
                     self.dequeue();
                 },
-                function(reason) { 
+                function (reason) {
                     console.error(reason);
                 }
             );
         };
 
         return Loader;
-    }());    
+    }());
 
     var loader = new Loader();
     BABYLONDEVTOOLS.Loader = loader;

+ 40 - 1
Tools/Gulp/config.json

@@ -1263,7 +1263,8 @@
         "serializers",
         "inspector",
         "gui",
-        "viewer"
+        "viewer",
+        "viewer-assets"
     ],
     "materialsLibrary": {
         "libraries": [
@@ -1719,6 +1720,7 @@
                     "../../gui/src/2D/controls/inputText.ts",
                     "../../gui/src/2D/controls/virtualKeyboard.ts",
                     "../../gui/src/2D/controls/multiLine.ts",
+                    "../../gui/src/2D/controls/grid.ts",                    
                     "../../gui/src/3D/gui3DManager.ts",
                     "../../gui/src/3D/materials/fluentMaterial.ts",
                     "../../gui/src/3D/vector3WithInfo.ts",
@@ -1853,6 +1855,7 @@
                 "name": "babylonjs-viewer",
                 "main": "../../Viewer/dist/build/src/index.d.ts",
                 "out": "../../../../dist/preview release/viewer/babylon.viewer.module.d.ts",
+                "legacyDeclaration": true,
                 "prependText": "/// <reference path=\"./babylon.d.ts\"/>\n/// <reference path=\"./babylon.glTF2Interface.d.ts\"/>\n/// <reference path=\"./babylonjs.loaders.d.ts\"/>\ndeclare module \"babylonjs-loaders\"{ export=BABYLON;}\n"
             },
             "outputs": [
@@ -1888,5 +1891,41 @@
                 }
             ]
         }
+    },
+    "viewer-assets": {
+        "libraries": [
+            {
+                "files": [],
+                "noBundleInName": true,
+                "output": "babylon.viewer.assets.js",
+                "webpack": "../../Viewer/webpack.assets.config.js",
+                "bundle": "true",
+                "moduleDeclaration": {
+                    "name": "BabylonViewerAssets",
+                    "module": "babylonjs-viewer-assets"
+                },
+                "babylonIncluded": true
+            }
+        ],
+        "build": {
+            "srcOutputDirectory": "../../Viewer/",
+            "dtsBundle": {
+                "name": "babylonjs-viewer-assets",
+                "baseDir": "../../Viewer/dist/build/src/assets/",
+                "main": "../../Viewer/dist/build/src/assets/index.d.ts",
+                "out": "../../../build/assets/babylon.viewer.assets.module.d.ts"
+            },
+            "outputs": [
+                {
+                    "destination": [
+                        {
+                            "filename": "babylon.viewer.assets.js",
+                            "outputDirectory": "/../../Viewer/dist/build/assets/"
+                        }
+                    ],
+                    "minified": true
+                }
+            ]
+        }
     }
 }

+ 5 - 3
Tools/Gulp/gulpfile.js

@@ -469,10 +469,12 @@ var buildExternalLibrary = function (library, settings, watch) {
                         let fileLocation = path.join(path.dirname(settings.build.dtsBundle.main), settings.build.dtsBundle.out);
                         fs.readFile(fileLocation, function (err, data) {
                             if (err) throw err;
-                            data = settings.build.dtsBundle.prependText + '\n' + data.toString();
+                            data = (settings.build.dtsBundle.prependText || "") + '\n' + data.toString();
                             fs.writeFile(fileLocation, data);
-                            var newData = processViewerDeclaration(data);
-                            fs.writeFile(fileLocation.replace('.module', ''), newData);
+                            if (settings.build.dtsBundle.legacyDeclaration) {
+                                var newData = processViewerDeclaration(data);
+                                fs.writeFile(fileLocation.replace('.module', ''), newData);
+                            }
                         });
                     });
                 }

+ 87 - 31
Tools/Publisher/index.js

@@ -1,6 +1,7 @@
 let prompt = require('prompt');
 let shelljs = require('shelljs');
 let fs = require('fs-extra');
+let path = require('path');
 
 let basePath = '../../dist/preview release';
 
@@ -46,7 +47,20 @@ let packages = [
     },
     {
         name: 'viewer',
-        path: basePath + '/viewer/'
+        path: basePath + '/../../Viewer/',
+        required: [
+            basePath + '/viewer/readme.md',
+            basePath + '/viewer/package.json',
+            basePath + '/viewer/babylon.viewer.js'
+        ]
+    },
+    {
+        name: 'viewer-assets',
+        path: basePath + '/../../Viewer/dist/build/assets/',
+        required: [
+            basePath + '/../../Viewer/assets/readme.md',
+            basePath + '/../../Viewer/assets/package.json',
+        ]
     }
 ];
 
@@ -71,7 +85,16 @@ function processPackages(version) {
     packages.forEach((package) => {
         if (package.name === "core") {
             processCore(package, version);
+        } else if (package.name === "viewer") {
+            processViewer(package, version);
         } else {
+
+            if (package.required) {
+                package.required.forEach(file => {
+                    fs.copySync(file, package.path + '/' + path.basename(file));
+                });
+            }
+
             let packageJson = require(package.path + 'package.json');
             packageJson.version = version;
             if (packageJson.dependencies) {
@@ -83,14 +106,8 @@ function processPackages(version) {
             }
             if (packageJson.peerDependencies) packageJson.peerDependencies.babylonjs = minimumDependency;
             fs.writeFileSync(package.path + 'package.json', JSON.stringify(packageJson, null, 4));
-            console.log('Publishing ' + package.name + " from " + package.path);
-            let tagDef = "";
-            // check for alpha or beta
-            if (version.indexOf('alpha') !== -1 || version.indexOf('beta') !== -1) {
-                tagDef = '--tag preview';
-            }
-            //publish the respected package
-            shelljs.exec('npm publish \"' + package.path + "\"" + ' ' + tagDef);
+
+            publish(version, package.name, package.path);
         }
 
     });
@@ -153,18 +170,6 @@ function processCore(package, version) {
         }
     ];
 
-    // remove the modules for now
-    /*fs.readdirSync(basePath + '/modules/').forEach(object => {
-        console.log(object);
-        if (fs.statSync(basePath + '/modules/' + object).isDirectory) {
-            files.push({
-                path: basePath + '/modules/' + object,
-                objectName: object,
-                isDir: true
-            });
-        }
-    })*/
-
     //copy them to the package path
     files.forEach(file => {
         fs.copySync(file.path, basePath + '/package/' + file.objectName);
@@ -189,16 +194,7 @@ function processCore(package, version) {
 
     fs.writeFileSync(basePath + '/package/' + 'package.json', JSON.stringify(packageJson, null, 4));
 
-    console.log('Publishing ' + package.name + " from " + basePath + '/package/');
-
-    let tagDef = "";
-    // check for alpha or beta
-    if (version.indexOf('alpha') !== -1 || version.indexOf('beta') !== -1) {
-        tagDef = '--tag preview';
-    }
-
-    //publish the respected package
-    shelljs.exec('npm publish \"' + basePath + '/package/' + "\"" + ' ' + tagDef);
+    publish(version, package.name, basePath + '/package/');
 
     // remove package directory
     fs.removeSync(basePath + '/package/');
@@ -217,3 +213,63 @@ function processCore(package, version) {
     fs.writeFileSync(package.path + 'package.json', JSON.stringify(packageJson, null, 4));
 }
 
+function processViewer(package, version) {
+
+    let buildPath = package.path + "dist/build/src/";
+    let projectPath = '../../Viewer';
+
+    if (package.required) {
+        package.required.forEach(file => {
+
+            fs.copySync(file, buildPath + '/' + path.basename(file));
+        });
+    }
+    // the viewer needs to be built using tsc on the viewer's main repository
+
+    // build the viewer
+    console.log("executing " + 'tsc -p ' + projectPath);
+    shelljs.exec('tsc -p ' + projectPath);
+
+    let packageJson = require(buildPath + '/package.json');
+
+    let files = getFiles(buildPath).map(f => f.replace(buildPath + "/", "")).filter(f => f.indexOf("assets/") === -1);
+
+    packageJson.files = files;
+    packageJson.version = version;
+    packageJson.module = "index.js";
+    packageJson.main = "babylon.viewer.js";
+    packageJson.typings = "index.d.ts";
+
+    fs.writeFileSync(buildPath + '/package.json', JSON.stringify(packageJson, null, 4));
+
+    publish(version, package.name, buildPath);
+
+}
+
+function publish(version, packageName, basePath) {
+    console.log('Publishing ' + packageName + " from " + basePath);
+
+    let tagDef = "";
+    // check for alpha or beta
+    if (version.indexOf('alpha') !== -1 || version.indexOf('beta') !== -1) {
+        tagDef = '--tag preview';
+    }
+
+    //publish the respected package
+    console.log("executing " + 'npm publish \"' + basePath + "\"" + ' ' + tagDef);
+    shelljs.exec('npm publish \"' + basePath + "\"" + ' ' + tagDef);
+}
+
+function getFiles(dir, files_) {
+    files_ = files_ || [];
+    var files = fs.readdirSync(dir);
+    for (var i in files) {
+        var name = dir + '/' + files[i];
+        if (fs.statSync(name).isDirectory()) {
+            getFiles(name, files_);
+        } else {
+            files_.push(name);
+        }
+    }
+    return files_;
+}

+ 31 - 0
Viewer/assets/package.json

@@ -0,0 +1,31 @@
+{
+    "author": {
+        "name": "Raanan Weber"
+    },
+    "name": "babylonjs-viewer-assets",
+    "description": "Compiled resources for the Babylon viewer.",
+    "version": "3.3.0-alpha.3",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/BabylonJS/Babylon.js.git"
+    },
+    "main": "babylon.viewer.assets.js",
+    "files": [
+        "babylon.viewer.assets.js",
+        "babylon.viewer.assets.module.d.ts",
+        "readme.md",
+        "package.json"
+    ],
+    "typings": "babylon.viewer.assets.module.d.ts",
+    "keywords": [
+        "3D",
+        "javascript",
+        "html5",
+        "webgl",
+        "viewer"
+    ],
+    "license": "Apache-2.0",
+    "engines": {
+        "node": "*"
+    }
+}

+ 19 - 0
Viewer/assets/readme.md

@@ -0,0 +1,19 @@
+# Babylon.js Viewer Assets
+
+Babylon's viewer assets package contains all needed binsry assets neeed for the proper operation of the viewer's templating system.
+
+This packes is only needed when intalling the viewer's npm package and is installed  and used automatically.
+
+For basic and advanced viewer usage instructions please read the doc at https://doc.babylonjs.com/extensions/the_babylon_viewer
+
+The source code can be found at https://github.com/BabylonJS/Babylon.js/tree/master/Viewer
+
+## Overriding the package
+
+To override the package when (for example) using webpack, define the package `babylonjs-viewer-assets` in "externals":
+
+```javascript
+externals: {
+    "babylonjs-viewer-assets": true
+}
+```

+ 1 - 1
Viewer/src/index.ts

@@ -3,7 +3,7 @@ import { viewerGlobals } from './configuration/globals';
 import { viewerManager } from './viewer/viewerManager';
 import { DefaultViewer } from './viewer/defaultViewer';
 import { AbstractViewer } from './viewer/viewer';
-import { telemetryManager } from './telemetryManager';
+import { telemetryManager } from './managers/telemetryManager';
 import { ModelLoader } from './loader/modelLoader';
 import { ViewerModel, ModelState } from './model/viewerModel';
 import { AnimationPlayMode, AnimationState } from './model/modelAnimation';

+ 2 - 2
Viewer/src/initializer.ts

@@ -1,6 +1,6 @@
 import { DefaultViewer } from './viewer/defaultViewer';
 import { mapperManager } from './configuration/mappers';
-import { viewerGlobals, disableInit } from './';
+import { viewerGlobals } from './configuration/globals';
 
 
 /**
@@ -11,7 +11,7 @@ export function initListeners() {
     document.addEventListener("DOMContentLoaded", init);
     function init(event) {
         document.removeEventListener("DOMContentLoaded", init);
-        if (viewerGlobals.disableInit || disableInit) return;
+        if (viewerGlobals.disableInit) return;
         InitTags();
     }
 }

+ 0 - 1
Viewer/src/loader/plugins/applyMaterialConfig.ts

@@ -1,5 +1,4 @@
 import { ILoaderPlugin } from "./loaderPlugin";
-import { telemetryManager } from "../../telemetryManager";
 import { ViewerModel } from "../../model/viewerModel";
 import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material } from "babylonjs";
 import { IGLTFLoaderData, GLTF2 } from "babylonjs-loaders";

+ 0 - 1
Viewer/src/loader/plugins/extendedMaterialLoaderPlugin.ts

@@ -1,5 +1,4 @@
 import { ILoaderPlugin } from "./loaderPlugin";
-import { telemetryManager } from "../../telemetryManager";
 import { ViewerModel } from "../../model/viewerModel";
 import { Color3, Texture, BaseTexture, Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync, Material, PBRMaterial, Engine } from "babylonjs";
 

+ 0 - 1
Viewer/src/loader/plugins/msftLodLoaderPlugin.ts

@@ -1,5 +1,4 @@
 import { ILoaderPlugin } from "./loaderPlugin";
-import { telemetryManager } from "../../telemetryManager";
 import { ViewerModel } from "../../model/viewerModel";
 import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
 import { IGLTFLoaderExtension, GLTF2 } from "babylonjs-loaders";

+ 1 - 1
Viewer/src/loader/plugins/telemetryLoaderPlugin.ts

@@ -1,5 +1,5 @@
 import { ILoaderPlugin } from "./loaderPlugin";
-import { telemetryManager } from "../../telemetryManager";
+import { telemetryManager } from "../../managers/telemetryManager";
 import { ViewerModel } from "../../model/viewerModel";
 import { Tools, ISceneLoaderPlugin, ISceneLoaderPluginAsync } from "babylonjs";
 

Viewer/src/telemetryManager.ts → Viewer/src/managers/telemetryManager.ts


+ 29 - 19
Viewer/src/viewer/defaultViewer.ts

@@ -26,6 +26,9 @@ export class DefaultViewer extends AbstractViewer {
         super(containerElement, initialConfiguration);
 
         this.onModelLoadedObservable.add(this._onModelLoaded);
+        this.onModelRemovedObservable.add(() => {
+            this._configureTemplate();
+        })
 
         this.onEngineInitObservable.add(() => {
             this.sceneManager.onLightsConfiguredObservable.add((data) => {
@@ -294,32 +297,39 @@ export class DefaultViewer extends AbstractViewer {
      * It is mainly responsible to changing the title and subtitle etc'.
      * @param model the model to be used to configure the templates by
      */
-    protected _configureTemplate(model: ViewerModel) {
+    protected _configureTemplate(model?: ViewerModel) {
         let navbar = this.templateManager.getTemplate('navBar');
         if (!navbar) return;
 
-        let newParams: any = {};
-
-        let animationNames = model.getAnimationNames();
-        if (animationNames.length >= 1) {
-            this._isAnimationPaused = (model.configuration.animation && !model.configuration.animation.autoStart) || !model.configuration.animation;
-            this._animationList = animationNames;
-            newParams.animations = this._animationList;
-            newParams.paused = this._isAnimationPaused;
-            let animationIndex = 0;
-            if (model.configuration.animation && typeof model.configuration.animation.autoStart === 'string') {
-                animationIndex = animationNames.indexOf(model.configuration.animation.autoStart);
-                if (animationIndex === -1) {
-                    animationIndex = 0;
+        let newParams: any = navbar.configuration.params || {};
+
+        if (!model) {
+            newParams.animations = null;
+        } else {
+
+            let animationNames = model.getAnimationNames();
+            newParams.animations = animationNames;
+            if (animationNames.length) {
+                this._isAnimationPaused = (model.configuration.animation && !model.configuration.animation.autoStart) || !model.configuration.animation;
+                this._animationList = animationNames;
+                newParams.paused = this._isAnimationPaused;
+                let animationIndex = 0;
+                if (model.configuration.animation && typeof model.configuration.animation.autoStart === 'string') {
+                    animationIndex = animationNames.indexOf(model.configuration.animation.autoStart);
+                    if (animationIndex === -1) {
+                        animationIndex = 0;
+                    }
                 }
+                this._updateAnimationType(animationNames[animationIndex], newParams);
+            } else {
+                newParams.animations = null;
             }
-            this._updateAnimationType(animationNames[animationIndex], newParams);
-        }
 
-        if (model.configuration.thumbnail) {
-            newParams.logoImage = model.configuration.thumbnail
+            if (model.configuration.thumbnail) {
+                newParams.logoImage = model.configuration.thumbnail
+            }
         }
-        navbar.updateParams(newParams);
+        navbar.updateParams(newParams, false);
     }
 
     /**

+ 1 - 1
Viewer/src/viewer/viewer.ts

@@ -10,7 +10,7 @@ import { ModelLoader } from '../loader/modelLoader';
 import { CameraBehavior } from '../interfaces';
 import { viewerGlobals } from '../configuration/globals';
 import { extendClassWithConfig } from '../helper';
-import { telemetryManager } from '../telemetryManager';
+import { telemetryManager } from '../managers/telemetryManager';
 import { deepmerge } from '../helper/';
 import { ObservablesManager } from '../managers/observablesManager';
 import { ConfigurationContainer } from '../configuration/configurationContainer';

+ 1 - 1
Viewer/tsconfig.json

@@ -1,7 +1,7 @@
 {
     "compilerOptions": {
         "target": "es2015",
-        "module": "es2015",
+        "module": "commonjs",
         "declaration": true,
         "experimentalDecorators": true,
         "emitDecoratorMetadata": true,

+ 42 - 0
Viewer/webpack.assets.config.js

@@ -0,0 +1,42 @@
+module.exports = {
+    entry: [
+        __dirname + '/src/assets/index.ts'
+    ],
+    output: {
+        libraryTarget: 'var',
+        library: 'BabylonViewerAssets',
+        umdNamedDefine: true
+    },
+    resolve: {
+        extensions: ['.ts']
+    },
+    module: {
+        loaders: [{
+            test: /\.tsx?$/,
+            use: {
+                loader: 'ts-loader',
+                options: {
+                    configFile: 'tsconfig-gulp.json'
+                }
+            },
+            exclude: /node_modules/
+        },
+        {
+            test: /\.(html)$/,
+            use: {
+                loader: 'html-loader',
+                options: {
+                    minimize: true
+                }
+            }
+        },
+        {
+            test: /\.(jpe?g|png|ttf|eot|svg?)(\?[a-z0-9=&.]+)?$/,
+            use: 'base64-image-loader?limit=1000&name=[name].[ext]'
+        },
+        {
+            test: /\.(woff|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
+            loader: 'base64-font-loader'
+        }]
+    }
+}

+ 1 - 2
Viewer/webpack.gulp.config.js

@@ -2,8 +2,7 @@ module.exports = {
     //context: __dirname,
     entry: [
         __dirname + '/src/index.ts'
-    ]
-    ,
+    ],
     output: {
         libraryTarget: 'var',
         library: 'BabylonViewer',

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2749 - 2667
dist/preview release/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 49 - 49
dist/preview release/babylon.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 169 - 25
dist/preview release/babylon.max.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 169 - 25
dist/preview release/babylon.no-module.max.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 31 - 31
dist/preview release/babylon.worker.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 169 - 25
dist/preview release/es6.js


+ 83 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -607,6 +607,8 @@ declare module BABYLON.GUI {
         private _enterCount;
         private _doNotRender;
         private _downPointerIds;
+        /** @hidden */
+        _tag: any;
         /** Gets or sets a boolean indicating if the control can be hit with pointer events */
         isHitTestVisible: boolean;
         /** Gets or sets a boolean indicating if the control can block pointer events */
@@ -1867,6 +1869,87 @@ declare module BABYLON.GUI {
 
 declare module BABYLON.GUI {
     /**
+     * Class used to create a 2D grid container
+     */
+    class Grid extends Container {
+        name: string | undefined;
+        private _rowDefinitions;
+        private _columnDefinitions;
+        private _cells;
+        private _childControls;
+        /** Gets the list of children */
+        readonly children: Control[];
+        /**
+         * Adds a new row to the grid
+         * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the height is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        addRowDefinition(height: number, isPixel?: boolean): Grid;
+        /**
+         * Adds a new column to the grid
+         * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the width is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        addColumnDefinition(width: number, isPixel?: boolean): Grid;
+        /**
+         * Update a row definition
+         * @param index defines the index of the row to update
+         * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the weight is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        setRowDefinition(index: number, height: number, isPixel?: boolean): Grid;
+        /**
+         * Update a column definition
+         * @param index defines the index of the column to update
+         * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the width is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        setColumnDefinition(index: number, width: number, isPixel?: boolean): Grid;
+        private _removeCell(cell, key);
+        private _offsetCell(previousKey, key);
+        /**
+         * Remove a column definition at specified index
+         * @param index defines the index of the column to remove
+         * @returns the current grid
+         */
+        removeColumnDefinition(index: number): Grid;
+        /**
+         * Remove a row definition at specified index
+         * @param index defines the index of the row to remove
+         * @returns the current grid
+         */
+        removeRowDefinition(index: number): Grid;
+        /**
+         * Adds a new control to the current grid
+         * @param control defines the control to add
+         * @param row defines the row where to add the control (0 by default)
+         * @param column defines the column where to add the control (0 by default)
+         * @returns the current grid
+         */
+        addControl(control: Control, row?: number, column?: number): Grid;
+        /**
+         * Removes a control from the current container
+         * @param control defines the control to remove
+         * @returns the current container
+         */
+        removeControl(control: Control): Container;
+        /**
+         * Creates a new Grid
+         * @param name defines control name
+         */
+        constructor(name?: string | undefined);
+        protected _getTypeName(): string;
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
      * Class used to manage 3D user interface
      * @see http://doc.babylonjs.com/how_to/gui3d
      */

+ 310 - 3
dist/preview release/gui/babylon.gui.js

@@ -2888,7 +2888,7 @@ var BABYLON;
              * @returns the child control if found
              */
             Container.prototype.getChildByName = function (name) {
-                for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
+                for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
                     var child = _a[_i];
                     if (child.name === name) {
                         return child;
@@ -2903,7 +2903,7 @@ var BABYLON;
              * @returns the child control if found
              */
             Container.prototype.getChildByType = function (name, type) {
-                for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
+                for (var _i = 0, _a = this.children; _i < _a.length; _i++) {
                     var child = _a[_i];
                     if (child.typeName === type) {
                         return child;
@@ -2917,7 +2917,7 @@ var BABYLON;
              * @returns true if the control is in child list
              */
             Container.prototype.containsControl = function (control) {
-                return this._children.indexOf(control) !== -1;
+                return this.children.indexOf(control) !== -1;
             };
             /**
              * Adds a new control to the current container
@@ -6477,6 +6477,313 @@ var BABYLON;
     })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
 })(BABYLON || (BABYLON = {}));
 
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+var BABYLON;
+(function (BABYLON) {
+    var GUI;
+    (function (GUI) {
+        /**
+         * Class used to create a 2D grid container
+         */
+        var Grid = /** @class */ (function (_super) {
+            __extends(Grid, _super);
+            /**
+             * Creates a new Grid
+             * @param name defines control name
+             */
+            function Grid(name) {
+                var _this = _super.call(this, name) || this;
+                _this.name = name;
+                _this._rowDefinitions = new Array();
+                _this._columnDefinitions = new Array();
+                _this._cells = {};
+                _this._childControls = new Array();
+                return _this;
+            }
+            Object.defineProperty(Grid.prototype, "children", {
+                /** Gets the list of children */
+                get: function () {
+                    return this._childControls;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            /**
+             * Adds a new row to the grid
+             * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+             * @param isPixel defines if the height is expressed in pixel (or in percentage)
+             * @returns the current grid
+             */
+            Grid.prototype.addRowDefinition = function (height, isPixel) {
+                if (isPixel === void 0) { isPixel = false; }
+                this._rowDefinitions.push(new GUI.ValueAndUnit(height, isPixel ? GUI.ValueAndUnit.UNITMODE_PIXEL : GUI.ValueAndUnit.UNITMODE_PERCENTAGE));
+                this._markAsDirty();
+                return this;
+            };
+            /**
+             * Adds a new column to the grid
+             * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+             * @param isPixel defines if the width is expressed in pixel (or in percentage)
+             * @returns the current grid
+             */
+            Grid.prototype.addColumnDefinition = function (width, isPixel) {
+                if (isPixel === void 0) { isPixel = false; }
+                this._columnDefinitions.push(new GUI.ValueAndUnit(width, isPixel ? GUI.ValueAndUnit.UNITMODE_PIXEL : GUI.ValueAndUnit.UNITMODE_PERCENTAGE));
+                this._markAsDirty();
+                return this;
+            };
+            /**
+             * Update a row definition
+             * @param index defines the index of the row to update
+             * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+             * @param isPixel defines if the weight is expressed in pixel (or in percentage)
+             * @returns the current grid
+             */
+            Grid.prototype.setRowDefinition = function (index, height, isPixel) {
+                if (isPixel === void 0) { isPixel = false; }
+                if (index < 0 || index >= this._rowDefinitions.length) {
+                    return this;
+                }
+                this._rowDefinitions[index] = new GUI.ValueAndUnit(height, isPixel ? GUI.ValueAndUnit.UNITMODE_PIXEL : GUI.ValueAndUnit.UNITMODE_PERCENTAGE);
+                this._markAsDirty();
+                return this;
+            };
+            /**
+             * Update a column definition
+             * @param index defines the index of the column to update
+             * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+             * @param isPixel defines if the width is expressed in pixel (or in percentage)
+             * @returns the current grid
+             */
+            Grid.prototype.setColumnDefinition = function (index, width, isPixel) {
+                if (isPixel === void 0) { isPixel = false; }
+                if (index < 0 || index >= this._columnDefinitions.length) {
+                    return this;
+                }
+                this._columnDefinitions[index] = new GUI.ValueAndUnit(width, isPixel ? GUI.ValueAndUnit.UNITMODE_PIXEL : GUI.ValueAndUnit.UNITMODE_PERCENTAGE);
+                this._markAsDirty();
+                return this;
+            };
+            Grid.prototype._removeCell = function (cell, key) {
+                if (!cell) {
+                    return;
+                }
+                _super.prototype.removeControl.call(this, cell);
+                for (var _i = 0, _a = cell.children; _i < _a.length; _i++) {
+                    var control = _a[_i];
+                    var childIndex = this._childControls.indexOf(control);
+                    if (childIndex !== -1) {
+                        this._childControls.splice(childIndex, 1);
+                    }
+                }
+                delete this._cells[key];
+            };
+            Grid.prototype._offsetCell = function (previousKey, key) {
+                if (!this._cells[key]) {
+                    return;
+                }
+                this._cells[previousKey] = this._cells[key];
+                for (var _i = 0, _a = this._cells[previousKey].children; _i < _a.length; _i++) {
+                    var control = _a[_i];
+                    control._tag = previousKey;
+                }
+                delete this._cells[key];
+            };
+            /**
+             * Remove a column definition at specified index
+             * @param index defines the index of the column to remove
+             * @returns the current grid
+             */
+            Grid.prototype.removeColumnDefinition = function (index) {
+                if (index < 0 || index >= this._columnDefinitions.length) {
+                    return this;
+                }
+                for (var x = 0; x < this._rowDefinitions.length; x++) {
+                    var key = x + ":" + index;
+                    var cell = this._cells[key];
+                    this._removeCell(cell, key);
+                }
+                for (var x = 0; x < this._rowDefinitions.length; x++) {
+                    for (var y = index + 1; y < this._columnDefinitions.length; y++) {
+                        var previousKey = x + ":" + (y - 1);
+                        var key = x + ":" + y;
+                        this._offsetCell(previousKey, key);
+                    }
+                }
+                this._columnDefinitions.splice(index, 1);
+                this._markAsDirty();
+                return this;
+            };
+            /**
+             * Remove a row definition at specified index
+             * @param index defines the index of the row to remove
+             * @returns the current grid
+             */
+            Grid.prototype.removeRowDefinition = function (index) {
+                if (index < 0 || index >= this._rowDefinitions.length) {
+                    return this;
+                }
+                for (var y = 0; y < this._columnDefinitions.length; y++) {
+                    var key = index + ":" + y;
+                    var cell = this._cells[key];
+                    this._removeCell(cell, key);
+                }
+                for (var y = 0; y < this._columnDefinitions.length; y++) {
+                    for (var x = index + 1; x < this._rowDefinitions.length; x++) {
+                        var previousKey = x - 1 + ":" + y;
+                        var key = x + ":" + y;
+                        this._offsetCell(previousKey, key);
+                    }
+                }
+                this._rowDefinitions.splice(index, 1);
+                this._markAsDirty();
+                return this;
+            };
+            /**
+             * Adds a new control to the current grid
+             * @param control defines the control to add
+             * @param row defines the row where to add the control (0 by default)
+             * @param column defines the column where to add the control (0 by default)
+             * @returns the current grid
+             */
+            Grid.prototype.addControl = function (control, row, column) {
+                if (row === void 0) { row = 0; }
+                if (column === void 0) { column = 0; }
+                if (this._rowDefinitions.length === 0) {
+                    // Add default row definition
+                    this.addRowDefinition(1, false);
+                }
+                if (this._columnDefinitions.length === 0) {
+                    // Add default column definition
+                    this.addColumnDefinition(1, false);
+                }
+                var x = Math.min(row, this._rowDefinitions.length - 1);
+                var y = Math.min(column, this._columnDefinitions.length - 1);
+                var key = x + ":" + y;
+                var goodContainer = this._cells[key];
+                if (!goodContainer) {
+                    goodContainer = new GUI.Container(key);
+                    this._cells[key] = goodContainer;
+                    goodContainer.horizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_LEFT;
+                    goodContainer.verticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_TOP;
+                    _super.prototype.addControl.call(this, goodContainer);
+                }
+                goodContainer.addControl(control);
+                this._childControls.push(control);
+                control._tag = key;
+                this._markAsDirty();
+                return this;
+            };
+            /**
+             * Removes a control from the current container
+             * @param control defines the control to remove
+             * @returns the current container
+             */
+            Grid.prototype.removeControl = function (control) {
+                var index = this._childControls.indexOf(control);
+                if (index !== -1) {
+                    this._childControls.splice(index, 1);
+                }
+                var cell = this._cells[control._tag];
+                if (cell) {
+                    cell.removeControl(control);
+                }
+                this._markAsDirty();
+                return this;
+            };
+            Grid.prototype._getTypeName = function () {
+                return "Grid";
+            };
+            Grid.prototype._additionalProcessing = function (parentMeasure, context) {
+                var widths = [];
+                var heights = [];
+                var lefts = [];
+                var tops = [];
+                var availableWidth = this._currentMeasure.width;
+                var globalWidthPercentage = 0;
+                var availableHeight = this._currentMeasure.height;
+                var globalHeightPercentage = 0;
+                // Heights
+                var index = 0;
+                for (var _i = 0, _a = this._rowDefinitions; _i < _a.length; _i++) {
+                    var value = _a[_i];
+                    if (value.isPixel) {
+                        var height = value.getValue(this._host);
+                        availableHeight -= height;
+                        heights[index] = height;
+                    }
+                    else {
+                        globalHeightPercentage += value.internalValue;
+                    }
+                    index++;
+                }
+                var top = 0;
+                index = 0;
+                for (var _b = 0, _c = this._rowDefinitions; _b < _c.length; _b++) {
+                    var value = _c[_b];
+                    tops.push(top);
+                    if (!value.isPixel) {
+                        var height = (value.internalValue / globalHeightPercentage) * availableHeight;
+                        top += height;
+                        heights[index] = height;
+                    }
+                    else {
+                        top += value.getValue(this._host);
+                    }
+                    index++;
+                }
+                // Widths
+                index = 0;
+                for (var _d = 0, _e = this._columnDefinitions; _d < _e.length; _d++) {
+                    var value = _e[_d];
+                    if (value.isPixel) {
+                        var width = value.getValue(this._host);
+                        availableWidth -= width;
+                        widths[index] = width;
+                    }
+                    else {
+                        globalWidthPercentage += value.internalValue;
+                    }
+                    index++;
+                }
+                var left = 0;
+                index = 0;
+                for (var _f = 0, _g = this._columnDefinitions; _f < _g.length; _f++) {
+                    var value = _g[_f];
+                    lefts.push(left);
+                    if (!value.isPixel) {
+                        var width = (value.internalValue / globalWidthPercentage) * availableWidth;
+                        left += width;
+                        widths[index] = width;
+                    }
+                    else {
+                        left += value.getValue(this._host);
+                    }
+                    index++;
+                }
+                // Setting child sizes
+                for (var key in this._cells) {
+                    if (!this._cells.hasOwnProperty(key)) {
+                        continue;
+                    }
+                    var split = key.split(":");
+                    var x = parseInt(split[0]);
+                    var y = parseInt(split[1]);
+                    var cell = this._cells[key];
+                    cell.left = lefts[y] + "px";
+                    cell.top = tops[x] + "px";
+                    cell.width = widths[y] + "px";
+                    cell.height = heights[x] + "px";
+                }
+                _super.prototype._additionalProcessing.call(this, parentMeasure, context);
+            };
+            return Grid;
+        }(GUI.Container));
+        GUI.Grid = Grid;
+    })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));
+})(BABYLON || (BABYLON = {}));
+
 /// <reference path="../../../dist/preview release/babylon.d.ts"/>
 var BABYLON;
 (function (BABYLON) {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 4 - 4
dist/preview release/gui/babylon.gui.min.js


+ 83 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -612,6 +612,8 @@ declare module BABYLON.GUI {
         private _enterCount;
         private _doNotRender;
         private _downPointerIds;
+        /** @hidden */
+        _tag: any;
         /** Gets or sets a boolean indicating if the control can be hit with pointer events */
         isHitTestVisible: boolean;
         /** Gets or sets a boolean indicating if the control can block pointer events */
@@ -1872,6 +1874,87 @@ declare module BABYLON.GUI {
 
 declare module BABYLON.GUI {
     /**
+     * Class used to create a 2D grid container
+     */
+    class Grid extends Container {
+        name: string | undefined;
+        private _rowDefinitions;
+        private _columnDefinitions;
+        private _cells;
+        private _childControls;
+        /** Gets the list of children */
+        readonly children: Control[];
+        /**
+         * Adds a new row to the grid
+         * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the height is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        addRowDefinition(height: number, isPixel?: boolean): Grid;
+        /**
+         * Adds a new column to the grid
+         * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the width is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        addColumnDefinition(width: number, isPixel?: boolean): Grid;
+        /**
+         * Update a row definition
+         * @param index defines the index of the row to update
+         * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the weight is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        setRowDefinition(index: number, height: number, isPixel?: boolean): Grid;
+        /**
+         * Update a column definition
+         * @param index defines the index of the column to update
+         * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the width is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        setColumnDefinition(index: number, width: number, isPixel?: boolean): Grid;
+        private _removeCell(cell, key);
+        private _offsetCell(previousKey, key);
+        /**
+         * Remove a column definition at specified index
+         * @param index defines the index of the column to remove
+         * @returns the current grid
+         */
+        removeColumnDefinition(index: number): Grid;
+        /**
+         * Remove a row definition at specified index
+         * @param index defines the index of the row to remove
+         * @returns the current grid
+         */
+        removeRowDefinition(index: number): Grid;
+        /**
+         * Adds a new control to the current grid
+         * @param control defines the control to add
+         * @param row defines the row where to add the control (0 by default)
+         * @param column defines the column where to add the control (0 by default)
+         * @returns the current grid
+         */
+        addControl(control: Control, row?: number, column?: number): Grid;
+        /**
+         * Removes a control from the current container
+         * @param control defines the control to remove
+         * @returns the current container
+         */
+        removeControl(control: Control): Container;
+        /**
+         * Creates a new Grid
+         * @param name defines control name
+         */
+        constructor(name?: string | undefined);
+        protected _getTypeName(): string;
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+    }
+}
+
+
+declare module BABYLON.GUI {
+    /**
      * Class used to manage 3D user interface
      * @see http://doc.babylonjs.com/how_to/gui3d
      */

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


+ 30 - 28
dist/preview release/inspector/babylon.inspector.js

@@ -4316,35 +4316,37 @@ var INSPECTOR;
                     }, undefined, true);
                 };
                 elemValue.appendChild(inputElement);
-                elemValue = INSPECTOR.Helpers.CreateDiv(null, _this._panel);
-                inputElement = INSPECTOR.Inspector.DOCUMENT.createElement('input');
-                inputElement.value = "Compress current texture to .env";
-                inputElement.className = "tool-input";
-                inputElement.type = "button";
-                inputElement.onclick = function () {
-                    if (!_this._scene.environmentTexture) {
-                        errorElemm_1.style.display = "block";
-                        errorElemm_1.textContent = "You must load an environment texture first.";
-                        return;
-                    }
-                    if (_this._scene.activeCamera) {
-                        BABYLON.EnvironmentTextureTools.CreateEnvTextureAsync(_this._scene.environmentTexture)
-                            .then(function (buffer) {
-                            var blob = new Blob([buffer], { type: "octet/stream" });
-                            BABYLON.Tools.Download(blob, "environment.env");
-                            errorElemm_1.style.display = "none";
-                        })
-                            .catch(function (error) {
+                if (!_this._scene.getEngine().premultipliedAlpha) {
+                    elemValue = INSPECTOR.Helpers.CreateDiv(null, _this._panel);
+                    inputElement = INSPECTOR.Inspector.DOCUMENT.createElement('input');
+                    inputElement.value = "Compress current texture to .env";
+                    inputElement.className = "tool-input";
+                    inputElement.type = "button";
+                    inputElement.onclick = function () {
+                        if (!_this._scene.environmentTexture) {
                             errorElemm_1.style.display = "block";
-                            errorElemm_1.textContent = error;
-                        });
-                    }
-                    else {
-                        errorElemm_1.style.display = "block";
-                        errorElemm_1.textContent = "An active camera is required.";
-                    }
-                };
-                elemValue.appendChild(inputElement);
+                            errorElemm_1.textContent = "You must load an environment texture first.";
+                            return;
+                        }
+                        if (_this._scene.activeCamera) {
+                            BABYLON.EnvironmentTextureTools.CreateEnvTextureAsync(_this._scene.environmentTexture)
+                                .then(function (buffer) {
+                                var blob = new Blob([buffer], { type: "octet/stream" });
+                                BABYLON.Tools.Download(blob, "environment.env");
+                                errorElemm_1.style.display = "none";
+                            })
+                                .catch(function (error) {
+                                errorElemm_1.style.display = "block";
+                                errorElemm_1.textContent = error;
+                            });
+                        }
+                        else {
+                            errorElemm_1.style.display = "block";
+                            errorElemm_1.textContent = "An active camera is required.";
+                        }
+                    };
+                    elemValue.appendChild(inputElement);
+                }
                 _this._panel.appendChild(errorElemm_1);
             }
             title = INSPECTOR.Helpers.CreateDiv('tool-title2', _this._panel);

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/inspector/babylon.inspector.min.js


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

@@ -181,7 +181,7 @@ declare module BabylonViewer {
                 * It is mainly responsible to changing the title and subtitle etc'.
                 * @param model the model to be used to configure the templates by
                 */
-            protected _configureTemplate(model: ViewerModel): void;
+            protected _configureTemplate(model?: ViewerModel): void;
             /**
                 * This will load a new model to the default viewer
                 * overriding the AbstractViewer's loadModel.

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 41 - 41
dist/preview release/viewer/babylon.viewer.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 320 - 165
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -14,7 +14,7 @@ declare module 'babylonjs-viewer' {
     import { viewerManager } from 'babylonjs-viewer/viewer/viewerManager';
     import { DefaultViewer } from 'babylonjs-viewer/viewer/defaultViewer';
     import { AbstractViewer } from 'babylonjs-viewer/viewer/viewer';
-    import { telemetryManager } from 'babylonjs-viewer/telemetryManager';
+    import { telemetryManager } from 'babylonjs-viewer/managers/telemetryManager';
     import { ModelLoader } from 'babylonjs-viewer/loader/modelLoader';
     import { ViewerModel, ModelState } from 'babylonjs-viewer/model/viewerModel';
     import { AnimationPlayMode, AnimationState } from 'babylonjs-viewer/model/modelAnimation';
@@ -181,7 +181,7 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * It is mainly responsible to changing the title and subtitle etc'.
                 * @param model the model to be used to configure the templates by
                 */
-            protected _configureTemplate(model: ViewerModel): void;
+            protected _configureTemplate(model?: ViewerModel): void;
             /**
                 * This will load a new model to the default viewer
                 * overriding the AbstractViewer's loadModel.
@@ -458,7 +458,7 @@ declare module 'babylonjs-viewer/viewer/viewer' {
     }
 }
 
-declare module 'babylonjs-viewer/telemetryManager' {
+declare module 'babylonjs-viewer/managers/telemetryManager' {
     import { Engine, Observable } from "babylonjs";
     /**
         * The data structure of a telemetry event.

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

@@ -31,5 +31,16 @@
     "license": "Apache-2.0",
     "engines": {
         "node": "*"
+    },
+    "dependencies": {
+        "deepmerge": "~2.1.1",
+        "handlebars": "~4.0.11",
+        "pepjs": "~0.4.3"
+    },
+    "peerDependencies": {
+        "babylonjs": ">3.3.0-alpha",
+        "babylonjs-loaders": ">3.3.0-alpha",
+        "babylonjs-gltf2interface": ">3.3.0-alpha",
+        "babylonjs-viewer-assets": ">3.3.0-alpha"
     }
 }

+ 6 - 2
dist/preview release/what's new.md

@@ -6,7 +6,8 @@
 - Added new `MixMaterial` to the Materials Library allowing to mix up to 8 textures ([julien-moreau](https://github.com/julien-moreau))
 - Added new `PhotoDome` object to display 360 photos. [Demo](https://www.babylonjs-playground.com/#14KRGG#0) ([SzeyinLee](https://github.com/SzeyinLee))
 - New GUI 3D controls toolset. [Complete doc + demos](http://doc.babylonjs.com/how_to/gui3d) ([Deltakosh](https://github.com/deltakosh))
-- Added Environmnent Texture Tools to reduce the size of the usual .DDS file ([sebavan](http://www.github.com/sebavan)). [How to](https://doc.babylonjs.com/how_to/physically_based_rendering#creating-a-compressed-environment-texture)
+- Added [Environmnent 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))
+- New GUI control: the [Grid](http://doc.babylonjs.com/how_to/gui#grid) ([Deltakosh](https://github.com/deltakosh))
 
 ## Updates
 
@@ -29,6 +30,7 @@
 - Gizmo and gizmoManager class used to manipulate meshes in a scene, position, rotation, scale gizmos ([TrevorDev](https://github.com/TrevorDev))
 - Added a new `mesh.ignoreNonUniformScaling` to turn off non uniform scaling compensation ([Deltakosh](https://github.com/deltakosh))
 - AssetsManager tasks will only run when their state is INIT. It is now possible to remove a task from the assets manager ([RaananW](https://github.com/RaananW))
+- Added support for `minScaleX`, `minScaleY`, `maxScaleX`, `maxScaleY` for particles ([Deltakosh](https://github.com/deltakosh))
 
 ### glTF Loader
 
@@ -44,7 +46,7 @@
 - Shadows will only render while models are entering the scene or animating ([RaananW](https://github.com/RaananW))
 - Support for model drag and drop onto the canvas ([RaananW](https://github.com/RaananW))
 - New lab feature - global light rotation [#4347](https://github.com/BabylonJS/Babylon.js/issues/4347) ([RaananW](https://github.com/RaananW))
-- New package - babylonjs-viewer-assets, to separate the binary assets and the code of the viewer ([RaananW](https://github.com/RaananW))
+- New NPM package - babylonjs-viewer-assets, to separate the binary assets and the code of the viewer ([RaananW](https://github.com/RaananW))
 
 ### Documentation
 
@@ -66,6 +68,7 @@
 - Fixed a bug with setting `unlit` on `PBRMaterial` after the material is ready (Wrong dirty flags) ([bghgary](http://www.github.com/bghgary))
 - Fixed `HighlightLayer` support on browsers not supporting HalfFloat ([sebavan](http://www.github.com/sebavan))
 - Fixed support for R and RG texture formats ([sebavan](http://www.github.com/sebavan))
+- Fixed `updatable` parameter setting in the SPS ([jerome](https://github.com/jbousquie))
 
 ### Viewer
 
@@ -76,6 +79,7 @@
 - Model configuration was not extended correctly if loaded more than one model ([RaananW](https://github.com/RaananW))
 - It wasn't possible to disable camera behavior(s) using configuration  [#4348](https://github.com/BabylonJS/Babylon.js/issues/4348) ([RaananW](https://github.com/RaananW))
 - Animation blending was always set to true, ignoring configuration [#4412](https://github.com/BabylonJS/Babylon.js/issues/4412) ([RaananW](https://github.com/RaananW))
+- Animation navbar now updates correctly when a new model is loaded [#4441](https://github.com/BabylonJS/Babylon.js/issues/4441) ([RaananW](https://github.com/RaananW))
 
 ## Breaking changes
 

+ 3 - 3
gui/src/2D/controls/container.ts

@@ -92,7 +92,7 @@ module BABYLON.GUI {
          * @returns the child control if found
          */
         public getChildByName(name: string): Nullable<Control> {
-            for (var child of this._children) {
+            for (var child of this.children) {
                 if (child.name === name) {
                     return child;
                 }
@@ -108,7 +108,7 @@ module BABYLON.GUI {
          * @returns the child control if found
          */        
         public getChildByType(name: string, type: string): Nullable<Control> {
-            for (var child of this._children) {
+            for (var child of this.children) {
                 if (child.typeName === type) {
                     return child;
                 }
@@ -123,7 +123,7 @@ module BABYLON.GUI {
          * @returns true if the control is in child list
          */
         public containsControl(control: Control): boolean {
-            return this._children.indexOf(control) !== -1;
+            return this.children.indexOf(control) !== -1;
         }
 
         /**

+ 3 - 0
gui/src/2D/controls/control.ts

@@ -72,6 +72,9 @@ module BABYLON.GUI {
         private _doNotRender = false;
         private _downPointerIds:{[id:number] : boolean} = {};
 
+        /** @hidden */
+        public _tag: any;
+
         /** Gets or sets a boolean indicating if the control can be hit with pointer events */
         public isHitTestVisible = true;
         /** Gets or sets a boolean indicating if the control can block pointer events */

+ 341 - 0
gui/src/2D/controls/grid.ts

@@ -0,0 +1,341 @@
+/// <reference path="../../../../dist/preview release/babylon.d.ts"/>
+
+module BABYLON.GUI {
+    /**
+     * Class used to create a 2D grid container
+     */
+    export class Grid extends Container {
+        private _rowDefinitions = new Array<ValueAndUnit>();
+        private _columnDefinitions = new Array<ValueAndUnit>();
+        private _cells: {[key: string]:Container} = {};
+        private _childControls = new Array<Control>();
+
+        /** Gets the list of children */
+        public get children(): Control[] {
+            return this._childControls;
+        }             
+
+        /**
+         * Adds a new row to the grid
+         * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the height is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        public addRowDefinition(height: number, isPixel = false): Grid {
+            this._rowDefinitions.push(new ValueAndUnit(height, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE));
+
+            this._markAsDirty();
+
+            return this;
+        }
+
+        /**
+         * Adds a new column to the grid
+         * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the width is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        public addColumnDefinition(width: number, isPixel = false): Grid {
+            this._columnDefinitions.push(new ValueAndUnit(width, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE));
+            
+            this._markAsDirty();
+
+            return this;
+        }     
+
+        /**
+         * Update a row definition
+         * @param index defines the index of the row to update
+         * @param height defines the height of the row (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the weight is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        public setRowDefinition(index: number, height: number, isPixel = false): Grid {
+            if (index < 0 || index >= this._rowDefinitions.length) {
+                return this;
+            }
+
+            this._rowDefinitions[index] = new ValueAndUnit(height,isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE);
+
+            this._markAsDirty();
+
+            return this;
+        }
+
+        /**
+         * Update a column definition
+         * @param index defines the index of the column to update
+         * @param width defines the width of the column (either in pixel or a value between 0 and 1)
+         * @param isPixel defines if the width is expressed in pixel (or in percentage)
+         * @returns the current grid
+         */
+        public setColumnDefinition(index: number, width: number, isPixel = false): Grid {
+            if (index < 0 || index >= this._columnDefinitions.length) {
+                return this;
+            }
+
+            this._columnDefinitions[index] = new ValueAndUnit(width, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE);
+
+            this._markAsDirty();
+
+            return this;
+        }        
+
+        private _removeCell(cell: Container, key: string) {
+            if (!cell) {
+                return;
+            }
+
+            super.removeControl(cell);
+
+            for (var control of cell.children) {
+                let childIndex = this._childControls.indexOf(control);
+
+                if (childIndex !== -1) {
+                    this._childControls.splice(childIndex, 1);
+                }
+            }
+
+            delete this._cells[key];
+        }
+
+        private _offsetCell(previousKey: string, key: string) {
+            if (!this._cells[key]) {
+                return;
+            }
+
+            this._cells[previousKey] = this._cells[key];
+
+            for (var control of this._cells[previousKey].children) {
+                control._tag = previousKey;
+            } 
+
+            delete this._cells[key];
+        }
+
+        /**
+         * Remove a column definition at specified index
+         * @param index defines the index of the column to remove
+         * @returns the current grid
+         */
+        public removeColumnDefinition(index: number): Grid {
+            if (index < 0 || index >= this._columnDefinitions.length) {
+                return this;
+            }
+
+            for (var x = 0; x < this._rowDefinitions.length; x++) {
+                let key = `${x}:${index}`;
+                let cell = this._cells[key];
+
+                this._removeCell(cell, key);
+            }
+
+            for (var x = 0; x < this._rowDefinitions.length; x++) {
+                for (var y = index + 1; y < this._columnDefinitions.length; y++) {
+                    let previousKey = `${x}:${y - 1}`;
+                    let key = `${x}:${y}`;
+
+                    this._offsetCell(previousKey, key);
+                }
+            }
+            
+            this._columnDefinitions.splice(index, 1);
+
+            this._markAsDirty();
+
+            return this;
+        }
+
+        /**
+         * Remove a row definition at specified index
+         * @param index defines the index of the row to remove
+         * @returns the current grid
+         */
+        public removeRowDefinition(index: number): Grid {
+            if (index < 0 || index >= this._rowDefinitions.length) {
+                return this;
+            }
+
+            for (var y = 0; y < this._columnDefinitions.length; y++) {
+                let key = `${index}:${y}`;
+                let cell = this._cells[key];
+
+                this._removeCell(cell, key);
+            }
+
+            for (var y = 0; y < this._columnDefinitions.length; y++) {
+                for (var x = index + 1; x < this._rowDefinitions.length; x++) {
+                    let previousKey = `${x - 1}:${y}`;
+                    let key = `${x}:${y}`;
+
+                    this._offsetCell(previousKey, key);
+                }
+            }
+            
+            this._rowDefinitions.splice(index, 1);
+
+            this._markAsDirty();
+
+            return this;
+        }
+
+        /**
+         * Adds a new control to the current grid
+         * @param control defines the control to add
+         * @param row defines the row where to add the control (0 by default)
+         * @param column defines the column where to add the control (0 by default)
+         * @returns the current grid
+         */
+        public addControl(control: Control, row: number = 0, column: number = 0): Grid {
+            if (this._rowDefinitions.length === 0) {
+                // Add default row definition
+                this.addRowDefinition(1, false);
+            }
+
+            if (this._columnDefinitions.length === 0) {
+                // Add default column definition
+                this.addColumnDefinition(1, false);
+            }
+
+            let x = Math.min(row, this._rowDefinitions.length - 1);
+            let y = Math.min(column, this._columnDefinitions.length - 1);
+            let key = `${x}:${y}`;
+            let goodContainer = this._cells[key];
+
+            if (!goodContainer) {
+                goodContainer = new Container(key);
+                this._cells[key] = goodContainer;
+                goodContainer.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
+                goodContainer.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
+                super.addControl(goodContainer);
+            }
+
+            goodContainer.addControl(control);
+            this._childControls.push(control);
+            control._tag = key;
+
+            this._markAsDirty();            
+
+            return this;
+        }
+
+        /**
+         * Removes a control from the current container
+         * @param control defines the control to remove
+         * @returns the current container
+         */        
+        public removeControl(control: Control): Container {
+            var index = this._childControls.indexOf(control);
+
+            if (index !== -1) {
+                this._childControls.splice(index, 1);
+            }
+
+            let cell = this._cells[control._tag];
+
+            if (cell) {
+                cell.removeControl(control);
+            }
+
+            this._markAsDirty();
+            return this;
+        }        
+
+        /**
+         * Creates a new Grid
+         * @param name defines control name
+         */
+        constructor(public name?: string) {
+            super(name);
+        }
+
+        protected _getTypeName(): string {
+            return "Grid";
+        }   
+
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+            let widths = [];
+            let heights = [];
+            let lefts = [];
+            let tops = [];
+
+            let availableWidth = this._currentMeasure.width;
+            let globalWidthPercentage = 0;
+            let availableHeight = this._currentMeasure.height;
+            let globalHeightPercentage = 0;
+
+            // Heights
+            let index = 0;
+            for (var value of this._rowDefinitions) {
+                if (value.isPixel) {
+                    let height = value.getValue(this._host);
+                    availableHeight -= height;
+                    heights[index] = height;
+                } else {
+                    globalHeightPercentage += value.internalValue;
+                }
+                index++;
+            }
+
+            let top = 0;
+            index = 0;
+            for (var value of this._rowDefinitions) {
+                tops.push(top);
+
+                if (!value.isPixel) {
+                    let height = (value.internalValue / globalHeightPercentage) * availableHeight;
+                    top += height;
+                    heights[index] = height;
+                } else {
+                    top += value.getValue(this._host);
+                }
+                index++;
+            }
+
+            // Widths
+            index = 0;
+            for (var value of this._columnDefinitions) {
+                if (value.isPixel) {
+                    let width = value.getValue(this._host);
+                    availableWidth -= width;
+                    widths[index] = width;
+                } else {
+                    globalWidthPercentage += value.internalValue;
+                }
+                index++;
+            }
+
+            let left = 0;
+            index = 0;
+            for (var value of this._columnDefinitions) {
+                lefts.push(left);
+                if (!value.isPixel) {
+                    let width = (value.internalValue / globalWidthPercentage) * availableWidth;
+                    left += width;
+                    widths[index] = width;
+                }  else {
+                    left += value.getValue(this._host);
+                }
+                index++;
+            }       
+            
+            // Setting child sizes
+            for (var key in this._cells) {
+                if (!this._cells.hasOwnProperty(key)) {
+                    continue;
+                }
+                let split = key.split(":");
+                let x = parseInt(split[0]);
+                let y = parseInt(split[1]);
+                let cell = this._cells[key];
+
+                cell.left = lefts[y] + "px";
+                cell.top = tops[x] + "px";
+                cell.width = widths[y] + "px";
+                cell.height = heights[x] + "px";
+            }
+
+            super._additionalProcessing(parentMeasure, context);
+        }
+    }
+}

+ 1 - 1
gui/src/3D/controls/control3D.ts

@@ -179,7 +179,7 @@ module BABYLON.GUI {
 
             let mesh = this.mesh;
             if (mesh) {
-                mesh.isVisible = value;
+                mesh.setEnabled(value);
             }
         }
 

+ 31 - 29
inspector/src/tabs/ToolsTab.ts

@@ -83,36 +83,38 @@ module INSPECTOR {
                 };
                 elemValue.appendChild(inputElement);
 
-                elemValue = Helpers.CreateDiv(null, this._panel);
-
-                inputElement = Inspector.DOCUMENT.createElement('input');
-                inputElement.value = "Compress current texture to .env";
-                inputElement.className = "tool-input";
-                inputElement.type = "button";
-                inputElement.onclick = () => {
-                    if (!this._scene.environmentTexture) {
-                        errorElemm.style.display = "block";
-                        errorElemm.textContent = "You must load an environment texture first.";
-                        return;
-                    }
-                    if (this._scene.activeCamera) {
-                        BABYLON.EnvironmentTextureTools.CreateEnvTextureAsync(<BABYLON.CubeTexture>this._scene.environmentTexture)
-                        .then((buffer: ArrayBuffer) => {
-                            var blob = new Blob([buffer], {type: "octet/stream"});
-                            BABYLON.Tools.Download(blob, "environment.env");
-                            errorElemm.style.display = "none";
-                        })
-                        .catch((error: any) => {
+                if (!this._scene.getEngine().premultipliedAlpha) {
+                    elemValue = Helpers.CreateDiv(null, this._panel);
+
+                    inputElement = Inspector.DOCUMENT.createElement('input');
+                    inputElement.value = "Compress current texture to .env";
+                    inputElement.className = "tool-input";
+                    inputElement.type = "button";
+                    inputElement.onclick = () => {
+                        if (!this._scene.environmentTexture) {
                             errorElemm.style.display = "block";
-                            errorElemm.textContent = error;
-                        });
-                    }
-                    else {
-                        errorElemm.style.display = "block";
-                        errorElemm.textContent = "An active camera is required.";
-                    }
-                };
-                elemValue.appendChild(inputElement);
+                            errorElemm.textContent = "You must load an environment texture first.";
+                            return;
+                        }
+                        if (this._scene.activeCamera) {
+                            BABYLON.EnvironmentTextureTools.CreateEnvTextureAsync(<BABYLON.CubeTexture>this._scene.environmentTexture)
+                            .then((buffer: ArrayBuffer) => {
+                                var blob = new Blob([buffer], {type: "octet/stream"});
+                                BABYLON.Tools.Download(blob, "environment.env");
+                                errorElemm.style.display = "none";
+                            })
+                            .catch((error: any) => {
+                                errorElemm.style.display = "block";
+                                errorElemm.textContent = error;
+                            });
+                        }
+                        else {
+                            errorElemm.style.display = "block";
+                            errorElemm.textContent = "An active camera is required.";
+                        }
+                    };
+                    elemValue.appendChild(inputElement);
+                }
                 
                 this._panel.appendChild(errorElemm);
             }

+ 102 - 1
src/Helpers/babylon.particleHelper.ts

@@ -48,6 +48,22 @@ module BABYLON {
          */
         maxSize: number;
         /**
+         * Minimum scale of each particle on X axis
+         */
+        minScaleX: number;
+        /**
+         * Maximum scale of each particle on X axis
+         */
+        maxScaleX: number;       
+        /**
+         * Minimum scale of each particle on Y axis
+         */
+        minScaleY: number;
+        /**
+         * Maximum scale of each particle on Y axis
+         */
+        maxScaleY: number;           
+        /**
          * Minimum lifetime for each particle
          */
         minLifeTime: number;
@@ -170,7 +186,9 @@ module BABYLON {
          */
         public static UpdateSystem(system: ParticleSystem, data: IParticleSystemData, scene: Scene): void {
             // Texture of each particle
-            system.particleTexture = new Texture(`${ParticleHelper._baseAssetsUrl}/textures/${data.textureFile}`, scene);
+            if (data.textureFile) {
+                system.particleTexture = new Texture(`${ParticleHelper._baseAssetsUrl}/textures/${data.textureFile}`, scene);
+            }
 
             // Colors of all particles
             system.color1 = new Color4(data.color1.r, data.color1.g, data.color1.b, data.color1.a);
@@ -181,6 +199,12 @@ module BABYLON {
             system.minSize = data.minSize;
             system.maxSize = data.maxSize;
 
+            system.minScaleX = data.minScaleX;
+            system.maxScaleX = data.maxScaleX;    
+            
+            system.minScaleY = data.minScaleY;
+            system.maxScaleY = data.maxScaleY;              
+
             // Life time of each particle (random between...
             system.minLifeTime = data.minLifeTime;
             system.maxLifeTime = data.maxLifeTime;
@@ -243,6 +267,83 @@ module BABYLON {
                     break;
             }
         }
+
+        /**
+         * Static function used to export a particle system to a IParticleSystemData variable.
+         * Please note that texture file name is not exported and must be added manually
+         * @param system defines the particle system to export
+         */
+        public static ExportSystem(system: ParticleSystem): IParticleSystemData {
+            var outData: any = {};
+
+            // Colors of all particles
+            outData.color1 = { r: system.color1.r, g: system.color1.g, b: system.color1.b, a: system.color1.a };
+            outData.color2 = { r: system.color2.r, g: system.color2.g, b: system.color2.b, a: system.color2.a };
+            outData.colorDead = { r: system.colorDead.r, g: system.colorDead.g, b: system.colorDead.b, a: system.colorDead.a };
+
+            // Size of each particle (random between...
+            outData.minSize = system.minSize;
+            outData.maxSize = system.maxSize;
+
+            outData.minScaleX = system.minScaleX;
+            outData.maxScaleX = system.maxScaleX;     
+            
+            outData.minScaleY = system.minScaleY;
+            outData.maxScaleY = system.maxScaleY;             
+
+            // Life time of each particle (random between...
+            outData.minLifeTime = system.minLifeTime;
+            outData.maxLifeTime = system.maxLifeTime;
+
+            // Emission rate
+            outData.emitRate = system.emitRate;
+
+            // Blend mode : BLENDMODE_ONEONE, or BLENDMODE_STANDARD
+            outData.blendMode = system.blendMode;
+
+            // Set the gravity of all particles
+            outData.gravity = {x: system.gravity.x, y: system.gravity.y, z: system.gravity.z};
+
+            // Angular speed, in radians
+            outData.minAngularSpeed = system.minAngularSpeed;
+            outData.maxAngularSpeed = system.maxAngularSpeed;
+
+            // Speed
+            outData.minEmitPower = system.minEmitPower;
+            outData.maxEmitPower = system.maxEmitPower;
+            outData.updateSpeed = system.updateSpeed;
+
+            
+            switch (system.particleEmitterType.getClassName()) {
+                case "BoxEmitter":
+                    outData.emitterType = "box";
+                    outData.direction1 = {x: system.direction1.x, y: system.direction1.y, z: system.direction1.z };
+                    outData.direction2 = {x: system.direction2.x, y: system.direction2.y, z: system.direction2.z };
+                    outData.minEmitBox = {x: system.minEmitBox.x, y: system.minEmitBox.y, z: system.minEmitBox.z };
+                    outData.maxEmitBox = {x: system.maxEmitBox.x, y: system.maxEmitBox.y, z: system.maxEmitBox.z };
+                    break;
+                case "SphereParticleEmitter":
+                    outData.emitterType = "sphere";
+                    outData.radius = (system.particleEmitterType as SphereParticleEmitter).radius;
+                    break;
+                case "SphereDirectedParticleEmitter":
+                    outData.emitterType = "directed_sphere";
+                    var sphereDirectedParticleEmitter = system.particleEmitterType as SphereDirectedParticleEmitter;
+                    outData.radius = sphereDirectedParticleEmitter.radius;
+                    outData.direction1 = {x: sphereDirectedParticleEmitter.direction1.x, y: sphereDirectedParticleEmitter.direction1.y, z: sphereDirectedParticleEmitter.direction1.z };
+                    outData.direction2 = {x: sphereDirectedParticleEmitter.direction2.x, y: sphereDirectedParticleEmitter.direction2.y, z: sphereDirectedParticleEmitter.direction2.z };                
+                    break;
+                case "ConeEmitter":
+                    outData.emitterType = "cone";
+                    outData.radius = (system.particleEmitterType as ConeParticleEmitter).radius;
+                    outData.angle = (system.particleEmitterType as ConeParticleEmitter).angle;
+                    break;
+                default:
+                    break;
+            }
+
+            return outData;
+        }
     }
 
 }

+ 7 - 0
src/Helpers/babylon.photoDome.ts

@@ -13,6 +13,13 @@ module BABYLON {
         protected _photoTexture: Texture;
 
         /**
+         * Gets the texture being displayed on the sphere
+         */
+        public get photoTexture(): Texture {
+            return this._photoTexture;
+        }        
+
+        /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;

+ 7 - 0
src/Helpers/babylon.videoDome.ts

@@ -13,6 +13,13 @@ module BABYLON {
         protected _videoTexture: VideoTexture;
 
         /**
+         * Gets the video texture being displayed on the sphere
+         */
+        public get videoTexture(): VideoTexture {
+            return this._videoTexture;
+        }
+
+        /**
          * The skybox material
          */
         protected _material: BackgroundMaterial;

+ 1 - 1
src/Particles/EmitterTypes/babylon.IParticleEmitterType.ts

@@ -51,7 +51,7 @@ module BABYLON {
          */        
         serialize(): any;
 
- /**
+        /**
          * Parse properties from a JSON object
          * @param serializationObject defines the JSON object
          */

+ 30 - 21
src/Particles/EmitterTypes/babylon.coneParticleEmitter.ts

@@ -6,22 +6,36 @@ module BABYLON {
      */
     export class ConeParticleEmitter implements IParticleEmitterType {
         private _radius: number;
+        private _angle: number;
         private _height: number;
 
         /**
-         * Gets the radius of the emission cone.
+         * Gets or sets the radius of the emission cone
          */
         public get radius(): number {
             return this._radius;
         }
 
-        /**
-         * Sets the radius of the emission cone.
-         */
         public set radius(value: number) {
             this._radius = value;
-            if (this.angle !== 0) {
-                this._height = value / Math.tan(this.angle / 2);
+            this._buildHeight();
+        }
+
+        /**
+         * Gets or sets the angle of the emission cone
+         */
+        public get angle(): number {
+            return this._angle;
+        }
+
+        public set angle(value: number) {
+            this._angle = value;
+            this._buildHeight();
+        }        
+
+        private _buildHeight() {
+            if (this._angle !== 0) {
+                this._height =  this._radius / Math.tan(this._angle / 2);
             }
             else {
                 this._height = 1;
@@ -32,17 +46,12 @@ module BABYLON {
          * Creates a new instance ConeParticleEmitter
          * @param radius the radius of the emission cone (1 by default)
          * @param angles the cone base angle (PI by default)
-         * @param directionRandomizer defines how much to randomize the particle direction [0-1]
+         * @param directionRandomizer defines how much to randomize the particle direction [0-1] (default is 0)
          */
-        constructor(radius = 1, 
-            /**
-             * The radius of the emission cone.
-             */
-            public angle = Math.PI, 
-            /**
-             * The cone base angle.
-             */
+        constructor(radius = 1, angle = Math.PI, 
+            /** defines how much to randomize the particle direction [0-1] (default is 0) */
             public directionRandomizer = 0) {
+            this.angle = angle;
             this.radius = radius;
         }
 
@@ -54,7 +63,7 @@ module BABYLON {
          * @param particle is the particle we are computed the direction for
          */
         public startDirectionFunction(emitPower: number, worldMatrix: Matrix, directionToUpdate: Vector3, particle: Particle): void {
-            if (this.angle === 0) {
+            if (this._angle === 0) {
                 Vector3.TransformNormalFromFloatsToRef(0, emitPower, 0, worldMatrix, directionToUpdate);
             }
             else {
@@ -98,7 +107,7 @@ module BABYLON {
          * @returns the new emitter
          */
         public clone(): ConeParticleEmitter {
-            let newOne = new ConeParticleEmitter(this.radius, this.angle, this.directionRandomizer);
+            let newOne = new ConeParticleEmitter(this._radius, this._angle, this.directionRandomizer);
 
             Tools.DeepCopy(this, newOne);
 
@@ -110,8 +119,8 @@ module BABYLON {
          * @param effect defines the update shader
          */        
         public applyToShader(effect: Effect): void {
-            effect.setFloat("radius", this.radius);
-            effect.setFloat("coneAngle", this.angle);
+            effect.setFloat("radius", this._radius);
+            effect.setFloat("coneAngle", this._angle);
             effect.setFloat("height", this._height);
             effect.setFloat("directionRandomizer", this.directionRandomizer);
         }
@@ -140,8 +149,8 @@ module BABYLON {
             var serializationObject: any = {};
 
             serializationObject.type = this.getClassName();
-            serializationObject.radius  = this.radius;
-            serializationObject.angle  = this.angle;
+            serializationObject.radius  = this._radius;
+            serializationObject.angle  = this._angle;
             serializationObject.directionRandomizer  = this.directionRandomizer;
 
             return serializationObject;

+ 19 - 1
src/Particles/babylon.IParticleSystem.ts

@@ -62,7 +62,25 @@ module BABYLON {
         /**
          * Maximum Size of emitting particles.
          */
-        maxSize: number;        
+        maxSize: number;     
+        
+        /**
+         * Minimum scale of emitting particles on X axis.
+         */
+        minScaleX: number;
+        /**
+         * Maximum scale of emitting particles on X axis.
+         */
+        maxScaleX: number;        
+
+        /**
+         * Minimum scale of emitting particles on Y axis.
+         */
+        minScaleY: number;
+        /**
+         * Maximum scale of emitting particles on Y axis.
+         */
+        maxScaleY: number;             
         
         /**
          * Random color of each particle after it has been emitted, between color1 and color2 vectors.

+ 31 - 10
src/Particles/babylon.gpuParticleSystem.ts

@@ -57,7 +57,7 @@
 
         private _randomTexture: RawTexture;
 
-        private readonly _attributesStrideSize = 16;
+        private readonly _attributesStrideSize = 18;
         private _updateEffectOptions: EffectCreationOptions;
 
         private _randomTextureSize: number;
@@ -119,7 +119,25 @@
         /**
          * Maximum Size of emitting particles.
          */
-        public maxSize = 1;        
+        public maxSize = 1;  
+        
+        /**
+         * Minimum scale of emitting particles on X axis.
+         */
+        public minScaleX = 1;
+        /**
+         * Maximum scale of emitting particles on X axis.
+         */
+        public maxScaleX = 1;        
+
+        /**
+         * Minimum scale of emitting particles on Y axis.
+         */
+        public minScaleY = 1;
+        /**
+         * Maximum scale of emitting particles on Y axis.
+         */
+        public maxScaleY = 1;           
         
         /**
          * Random color of each particle after it has been emitted, between color1 and color2 vectors.
@@ -356,7 +374,7 @@
 
             this._updateEffectOptions = {
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "angle"],
-                uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "gravity", "emitPower",
+                uniformsNames: ["currentCount", "timeDelta", "generalRandoms", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "angleRange"],
                 uniformBuffersNames: [],
@@ -393,10 +411,10 @@
             updateVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1);
             updateVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1);
             updateVertexBuffers["seed"] = source.createVertexBuffer("seed", 5, 1);
-            updateVertexBuffers["size"] = source.createVertexBuffer("size", 6, 1);
-            updateVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4);
-            updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 11, 3);
-            updateVertexBuffers["angle"] = source.createVertexBuffer("angle", 14, 2);
+            updateVertexBuffers["size"] = source.createVertexBuffer("size", 6, 3);
+            updateVertexBuffers["color"] = source.createVertexBuffer("color", 9, 4);
+            updateVertexBuffers["direction"] = source.createVertexBuffer("direction", 13, 3);
+            updateVertexBuffers["angle"] = source.createVertexBuffer("angle", 16, 2);
            
             let vao = this._engine.recordVertexArrayObject(updateVertexBuffers, null, this._updateEffect);
             this._engine.bindArrayBuffer(null);
@@ -409,9 +427,9 @@
             renderVertexBuffers["position"] = source.createVertexBuffer("position", 0, 3, this._attributesStrideSize, true);
             renderVertexBuffers["age"] = source.createVertexBuffer("age", 3, 1, this._attributesStrideSize, true);
             renderVertexBuffers["life"] = source.createVertexBuffer("life", 4, 1, this._attributesStrideSize, true);
-            renderVertexBuffers["size"] = source.createVertexBuffer("size", 6, 1, this._attributesStrideSize, true);           
-            renderVertexBuffers["color"] = source.createVertexBuffer("color", 7, 4, this._attributesStrideSize, true);
-            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", 14, 2, this._attributesStrideSize, true);
+            renderVertexBuffers["size"] = source.createVertexBuffer("size", 6, 3, this._attributesStrideSize, true);           
+            renderVertexBuffers["color"] = source.createVertexBuffer("color", 9, 4, this._attributesStrideSize, true);
+            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", 16, 2, this._attributesStrideSize, true);
 
             renderVertexBuffers["offset"] = spriteSource.createVertexBuffer("offset", 0, 2);
             renderVertexBuffers["uv"] = spriteSource.createVertexBuffer("uv", 2, 2);
@@ -444,6 +462,8 @@
 
               // Size
               data.push(0.0);
+              data.push(0.0);
+              data.push(0.0);
 
               // color
               data.push(0.0);
@@ -571,6 +591,7 @@
             this._updateEffect.setDirectColor4("color1", this.color1);
             this._updateEffect.setDirectColor4("color2", this.color2);
             this._updateEffect.setFloat2("sizeRange", this.minSize, this.maxSize);
+            this._updateEffect.setFloat4("scaleRange", this.minScaleX, this.maxScaleX, this.minScaleY, this.maxScaleY);
             this._updateEffect.setFloat2("angleRange", this.minAngularSpeed, this.maxAngularSpeed);
             this._updateEffect.setVector3("gravity", this.gravity);
 

+ 6 - 0
src/Particles/babylon.particle.ts

@@ -41,6 +41,11 @@
         public size = 0;
 
         /**
+         * The current scale of the particle.
+         */
+        public scale = new Vector2(1, 1);        
+
+        /**
          * The current angle of the particle.
          */
         public angle = 0;
@@ -134,6 +139,7 @@
             other.lifeTime = this.lifeTime;
             other.age = this.age;
             other.size = this.size;
+            other.scale.copyFrom(this.scale);
             other.angle = this.angle;
             other.angularSpeed = this.angularSpeed;
             other.particleSystem = this.particleSystem;

+ 49 - 14
src/Particles/babylon.particleSystem.ts

@@ -93,6 +93,24 @@
         public maxSize = 1;
 
         /**
+         * Minimum scale of emitting particles on X axis.
+         */
+        public minScaleX = 1;
+        /**
+         * Maximum scale of emitting particles on X axis.
+         */
+        public maxScaleX = 1;        
+
+        /**
+         * Minimum scale of emitting particles on Y axis.
+         */
+        public minScaleY = 1;
+        /**
+         * Maximum scale of emitting particles on Y axis.
+         */
+        public maxScaleY = 1;           
+
+        /**
          * Minimum angular speed of emitting particles (Z-axis rotation for each particle).
          */
         public minAngularSpeed = 0;
@@ -330,7 +348,7 @@
         private _stopped = false;
         private _actualFrame = 0;
         private _scaledUpdateSpeed: number;
-        private _vertexBufferSize = 11;
+        private _vertexBufferSize = 12;
         private _isAnimationSheetEnabled: boolean;
 
         // end of sheet animation
@@ -382,7 +400,7 @@
             this._epsilon = epsilon;
             this._isAnimationSheetEnabled = isAnimationSheetEnabled;
             if (isAnimationSheetEnabled) {
-                this._vertexBufferSize = 12;
+                this._vertexBufferSize = 13;
             }
 
             this._scene = scene || Engine.LastCreatedScene;
@@ -393,22 +411,24 @@
 
             this._createIndexBuffer();
 
-            // 11 floats per particle (x, y, z, r, g, b, a, angle, size, offsetX, offsetY) + 1 filler
+            // 13 floats per particle (x, y, z, r, g, b, a, angle, scaleX, scaleY, offsetX, offsetY) + 1 filler
             this._vertexData = new Float32Array(capacity * this._vertexBufferSize * 4);
             this._vertexBuffer = new Buffer(scene.getEngine(), this._vertexData, true, this._vertexBufferSize);
 
             var positions = this._vertexBuffer.createVertexBuffer(VertexBuffer.PositionKind, 0, 3);
             var colors = this._vertexBuffer.createVertexBuffer(VertexBuffer.ColorKind, 3, 4);
-            var options = this._vertexBuffer.createVertexBuffer("options", 7, 4);
+            var options = this._vertexBuffer.createVertexBuffer("options", 7, 3);
+            var size = this._vertexBuffer.createVertexBuffer("size", 10, 2);
 
             if (this._isAnimationSheetEnabled) {
-                var cellIndexBuffer = this._vertexBuffer.createVertexBuffer("cellIndex", 11, 1);
+                var cellIndexBuffer = this._vertexBuffer.createVertexBuffer("cellIndex", 12, 1);
                 this._vertexBuffers["cellIndex"] = cellIndexBuffer;
             }
 
             this._vertexBuffers[VertexBuffer.PositionKind] = positions;
             this._vertexBuffers[VertexBuffer.ColorKind] = colors;
             this._vertexBuffers["options"] = options;
+            this._vertexBuffers["size"] = size;
 
             // Default emitter type
             this.particleEmitterType = new BoxParticleEmitter();
@@ -534,9 +554,10 @@
             this._vertexData[offset + 5] = particle.color.b;
             this._vertexData[offset + 6] = particle.color.a;
             this._vertexData[offset + 7] = particle.angle;
-            this._vertexData[offset + 8] = particle.size;
-            this._vertexData[offset + 9] = offsetX;
-            this._vertexData[offset + 10] = offsetY;
+            this._vertexData[offset + 8] = offsetX;
+            this._vertexData[offset + 9] = offsetY;   
+            this._vertexData[offset + 10] = particle.scale.x * particle.size;
+            this._vertexData[offset + 11] = particle.scale.y * particle.size;      
         }
 
         /**
@@ -562,10 +583,11 @@
             this._vertexData[offset + 5] = particle.color.b;
             this._vertexData[offset + 6] = particle.color.a;
             this._vertexData[offset + 7] = particle.angle;
-            this._vertexData[offset + 8] = particle.size;
-            this._vertexData[offset + 9] = offsetX;
-            this._vertexData[offset + 10] = offsetY;
-            this._vertexData[offset + 11] = particle.cellIndex;
+            this._vertexData[offset + 8] = offsetX;
+            this._vertexData[offset + 9] = offsetY;   
+            this._vertexData[offset + 10] = particle.scale.x * particle.size;
+            this._vertexData[offset + 11] = particle.scale.y * particle.size;
+            this._vertexData[offset + 12] = particle.cellIndex;
         }
 
         // start of sub system methods
@@ -677,6 +699,7 @@
                 particle.lifeTime = Scalar.RandomRange(this.minLifeTime, this.maxLifeTime);
 
                 particle.size = Scalar.RandomRange(this.minSize, this.maxSize);
+                particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
                 particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
 
                 var step = Scalar.RandomRange(0, 1.0);
@@ -712,11 +735,11 @@
                 var effectCreationOption: any;
 
                 if (this._isAnimationSheetEnabled) {
-                    attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options", "cellIndex"];
+                    attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options", "size", "cellIndex"];
                     effectCreationOption = ["invView", "view", "projection", "particlesInfos", "vClipPlane", "textureMask"];
                 }
                 else {
-                    attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options"];
+                    attributesNamesOrOptions = [VertexBuffer.PositionKind, VertexBuffer.ColorKind, "options", "size", ];
                     effectCreationOption = ["invView", "view", "projection", "vClipPlane", "textureMask"]
                 }
 
@@ -1074,6 +1097,10 @@
             serializationObject.maxAngularSpeed = this.maxAngularSpeed;
             serializationObject.minSize = this.minSize;
             serializationObject.maxSize = this.maxSize;
+            serializationObject.minScaleX = this.minScaleX;
+            serializationObject.maxScaleX = this.maxScaleX;
+            serializationObject.minScaleY = this.minScaleY;
+            serializationObject.maxScaleY = this.maxScaleY;            
             serializationObject.minEmitPower = this.minEmitPower;
             serializationObject.maxEmitPower = this.maxEmitPower;
             serializationObject.minLifeTime = this.minLifeTime;
@@ -1169,6 +1196,14 @@
             particleSystem.maxAngularSpeed = parsedParticleSystem.maxAngularSpeed;
             particleSystem.minSize = parsedParticleSystem.minSize;
             particleSystem.maxSize = parsedParticleSystem.maxSize;
+
+            if (parsedParticleSystem.minScaleX) {
+                particleSystem.minScaleX = parsedParticleSystem.minScaleX;
+                particleSystem.maxScaleX = parsedParticleSystem.maxScaleX;                
+                particleSystem.minScaleY = parsedParticleSystem.minScaleY;
+                particleSystem.maxScaleY = parsedParticleSystem.maxScaleY;                
+            }
+
             particleSystem.minLifeTime = parsedParticleSystem.minLifeTime;
             particleSystem.maxLifeTime = parsedParticleSystem.maxLifeTime;
             particleSystem.minEmitPower = parsedParticleSystem.minEmitPower;

+ 1 - 1
src/Particles/babylon.solidParticleSystem.ts

@@ -159,7 +159,7 @@
                 this._particlesIntersect = options ? <boolean>options.particleIntersection : false;
                 this._bSphereOnly= options ? <boolean>options.boundingSphereOnly : false;
                 this._bSphereRadiusFactor = (options && options.bSphereRadiusFactor) ? options.bSphereRadiusFactor : 1.0;
-                if (options && options.updatable) {
+                if (options && options.updatable !== undefined) {
                     this._updatable = options.updatable;
                 } else {
                     this._updatable = true;

+ 2 - 2
src/Shaders/gpuRenderParticles.vertex.fx

@@ -8,7 +8,7 @@ uniform mat4 projection;
 in vec3 position;
 in float age;
 in float life;
-in float size;
+in vec3 size;
 in vec4 color;
 in vec2 offset;
 in vec2 uv;
@@ -29,7 +29,7 @@ void main() {
   float ratio = age / life;
   vColor = color * vec4(1.0 - ratio) + colorDead * vec4(ratio);
 
-  vec2 cornerPos = offset * size;
+  vec2 cornerPos = offset * size.yz * size.x;
 
   // Rotate
 	vec4 rotatedCorner;

+ 7 - 4
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -10,6 +10,7 @@ uniform mat4 emitterWM;
 uniform vec2 lifeTime;
 uniform vec2 emitPower;
 uniform vec2 sizeRange;
+uniform vec4 scaleRange;
 uniform vec4 color1;
 uniform vec4 color2;
 uniform vec3 gravity;
@@ -45,7 +46,7 @@ in vec3 position;
 in float age;
 in float life;
 in float seed;
-in float size;
+in vec3 size;
 in vec4 color;
 in vec3 direction;
 in vec2 angle;
@@ -55,7 +56,7 @@ out vec3 outPosition;
 out float outAge;
 out float outLife;
 out float outSeed;
-out float outSize;
+out vec3 outSize;
 out vec4 outColor;
 out vec3 outDirection;
 out vec2 outAngle;
@@ -77,7 +78,7 @@ void main() {
       outLife = life;
       outSeed = seed;
       outColor = vec4(0.,0.,0.,0.);
-      outSize = 0.;
+      outSize = vec3(0., 0., 0.);
       outDirection = direction;
       outAngle = angle;
       return;
@@ -96,7 +97,9 @@ void main() {
     outSeed = seed;
 
     // Size
-    outSize = sizeRange.x + (sizeRange.y - sizeRange.x) * randoms.g;
+    outSize.x = sizeRange.x + (sizeRange.y - sizeRange.x) * randoms.g;
+    outSize.y = scaleRange.x + (scaleRange.y - scaleRange.x) * randoms.b;
+    outSize.z = scaleRange.z + (scaleRange.w - scaleRange.z) * randoms.a;
 
     // Color
     outColor = color1 + (color2 - color1) * randoms.b;

+ 3 - 3
src/Shaders/particles.vertex.fx

@@ -1,7 +1,8 @@
 // Attributes
 attribute vec3 position;
 attribute vec4 color;
-attribute vec4 options;
+attribute vec3 options;
+attribute vec2 size;
 attribute float cellIndex;
 
 // Uniforms
@@ -22,9 +23,8 @@ varying float fClipDistance;
 void main(void) {	
 	vec3 viewPos = (view * vec4(position, 1.0)).xyz; 
 	vec2 cornerPos;
-	float size = options.y;
 	float angle = options.x;
-	vec2 offset = options.zw;
+	vec2 offset = options.yz;
 	
 	cornerPos = vec2(offset.x - 0.5, offset.y  - 0.5) * size;