浏览代码

Merge remote-tracking branch 'BabylonJS/master' into modules-extra-work

Raanan Weber 7 年之前
父节点
当前提交
f8d7fd7754
共有 100 个文件被更改,包括 79390 次插入14441 次删除
  1. 9 5
      .travis.yml
  2. 6 4
      .vscode/launch.json
  3. 4878 4921
      Playground/babylon.d.txt
  4. 6 12
      Tools/Gulp/config.json
  5. 711 0
      Tools/Gulp/gulp-validateTypedoc.js
  6. 109 28
      Tools/Gulp/gulpfile.js
  7. 15 11
      Tools/Gulp/package.json
  8. 2888 2931
      dist/preview release/babylon.d.ts
  9. 49 49
      dist/preview release/babylon.js
  10. 1410 709
      dist/preview release/babylon.max.js
  11. 51 51
      dist/preview release/babylon.worker.js
  12. 3516 3559
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  13. 53 53
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  14. 1440 709
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  15. 2 2
      dist/preview release/gui/babylon.gui.js
  16. 3 3
      dist/preview release/gui/babylon.gui.min.js
  17. 4 4
      dist/preview release/inspector/babylon.inspector.bundle.js
  18. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  19. 1 0
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  20. 22 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  21. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  22. 1 0
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  23. 30 0
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  24. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  25. 1 0
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  26. 30 0
      dist/preview release/loaders/babylon.glTFFileLoader.js
  27. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  28. 1 0
      dist/preview release/loaders/babylon.objFileLoader.d.ts
  29. 9 0
      dist/preview release/loaders/babylon.objFileLoader.js
  30. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  31. 1 0
      dist/preview release/loaders/babylon.stlFileLoader.d.ts
  32. 9 0
      dist/preview release/loaders/babylon.stlFileLoader.js
  33. 1 1
      dist/preview release/loaders/babylon.stlFileLoader.min.js
  34. 48 0
      dist/preview release/loaders/babylonjs.loaders.js
  35. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  36. 3 0
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  37. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  38. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  39. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  40. 3 3
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  41. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  42. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  43. 1 1
      dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js
  44. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  45. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  46. 62374 0
      dist/preview release/typedocValidationBaseline.json
  47. 63 62
      dist/preview release/viewer/babylon.viewer.js
  48. 1 0
      dist/preview release/what's new.md
  49. 4 5
      gui/src/controls/slider.ts
  50. 10 0
      loaders/src/OBJ/babylon.objFileLoader.ts
  51. 10 0
      loaders/src/STL/babylon.stlFileLoader.ts
  52. 10 2
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  53. 23 0
      loaders/src/glTF/babylon.glTFFileLoader.ts
  54. 2 2
      readme.md
  55. 20 5
      src/Animations/babylon.animation.ts
  56. 4 0
      src/Animations/babylon.runtimeAnimation.ts
  57. 13 4
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  58. 38 20
      src/Engine/babylon.engine.ts
  59. 41 0
      src/Gamepad/Controllers/babylon.gearVRController.ts
  60. 5 0
      src/Gamepad/Controllers/babylon.poseEnabledController.ts
  61. 39 2
      src/Layer/babylon.highlightlayer.ts
  62. 21 11
      src/Lights/Shadows/babylon.shadowGenerator.ts
  63. 1 1
      src/Lights/babylon.light.ts
  64. 480 418
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  65. 87 0
      src/Loading/babylon.sceneLoader.ts
  66. 0 280
      src/Math/babylon.math.SIMD.ts
  67. 13 5
      src/Mesh/babylon.instancedMesh.ts
  68. 68 6
      src/Mesh/babylon.mesh.ts
  69. 0 3
      src/Mesh/babylon.transformNode.ts
  70. 4 9
      src/Morph/babylon.morphTargetManager.ts
  71. 2 2
      src/Particles/babylon.solidParticleSystem.ts
  72. 8 0
      src/PostProcess/babylon.postProcess.ts
  73. 2 2
      src/PostProcess/babylon.volumetricLightScatteringPostProcess.ts
  74. 486 63
      src/Tools/babylon.sceneOptimizer.ts
  75. 119 0
      src/babylon.assetContainer.ts
  76. 0 426
      src/babylon.mixins.ts
  77. 99 6
      src/babylon.scene.ts
  78. 二进制
      tests/validation/ReferenceImages/Billboard.png
  79. 二进制
      tests/validation/ReferenceImages/DefaultRenderingPipeline.png
  80. 二进制
      tests/validation/ReferenceImages/Flat2009.png
  81. 二进制
      tests/validation/ReferenceImages/GUI.png
  82. 二进制
      tests/validation/ReferenceImages/Hillvalley.png
  83. 二进制
      tests/validation/ReferenceImages/SpaceDeK.png
  84. 二进制
      tests/validation/ReferenceImages/TheCar.png
  85. 二进制
      tests/validation/ReferenceImages/advancedShadows.png
  86. 二进制
      tests/validation/ReferenceImages/assetContainer.png
  87. 二进制
      tests/validation/ReferenceImages/charting.png
  88. 二进制
      tests/validation/ReferenceImages/customRTT.png
  89. 二进制
      tests/validation/ReferenceImages/instancedBones.png
  90. 二进制
      tests/validation/ReferenceImages/lod.png
  91. 二进制
      tests/validation/ReferenceImages/mansion.png
  92. 二进制
      tests/validation/ReferenceImages/normals.png
  93. 二进制
      tests/validation/ReferenceImages/pbrrough.png
  94. 二进制
      tests/validation/ReferenceImages/pointLightShadows.png
  95. 二进制
      tests/validation/ReferenceImages/procedural.png
  96. 二进制
      tests/validation/ReferenceImages/refprobe.png
  97. 二进制
      tests/validation/ReferenceImages/selfShadowing.png
  98. 二进制
      tests/validation/ReferenceImages/softShadows.png
  99. 11 30
      tests/validation/config.json
  100. 0 0
      tests/validation/karma.conf.browserstack.js

+ 9 - 5
.travis.yml

@@ -1,16 +1,20 @@
+sudo: required
+dist: trusty
 language: node_js
 node_js:
 - '6'
-addons:
-  sauce_connect:
-    username: vandenberghe.sebastien@gmail.com
-  jwt:
-    secure: kbaJ3o6cVqpB8Gzfd0yDSgGwH+SQXukevtpjMQ8Gip+N6FOumQxihPDqsqYKxc8svvR024nDmkIhbjPjz/YguvC3WUgYW4xk3Za7P7C9cpj2fYdrFuO7pD4sd/fNdUCqvKQ8jcxlIq4eEdBuoBTOHsP9J5KH7Z1M7e58atkx0o8=
 before_script:
 - npm install -g gulp
 - cd ./Tools/Gulp
 - npm install
+- "export DISPLAY=:99.0"
+- "sh -e /etc/init.d/xvfb start"
+- sleep 3 # give xvfb some time to start
 script: gulp
 notifications:
   slack:
     secure: TBYDAN8Dlkx3dM+Q5ClAZem7agAhQ1oB/fGT665qn7D+j2YfWChvlfXegvXL4LPDmQgbI0UfazcjWId5a0EwmmPkRb+kMJItPiMt5jiIp2WKoZQ+qob6H9tBCRJbbpWM430wiPeKfBfbcZP/XSlpVMWhgU5ogAFDSUKjvHT7IuE=
+env:
+  global:
+  - secure: XVMhWPm3ByrZduLmbyBMsKU462bEbYPNRp+42HcdOuC3YOl2ODPvANrE/qZneZzGqlyiFAg6X4mDGD71Qw9ekbFt5q0PmvIxkRrX0KNJEJl9/gnFadOz3xFWEfFw4tiSHp36PUu9aT6ti9mn9gnM8C+vQZhqVpaMgkLpWzeEeXQ=
+  - secure: jaGc/NJwACWJPLfI/L1UfYvWKX6AGVjFwXA+31elOVNAmG4XRwaTUaugqP4FhjTw0g2pqTB1BlSML+Fs3dVaUGy3bpslQLL74iG06XZbFUqrtVV2/sOcJaoWxIc69ld0tcqLdPvMSrCIzGQYAWaNNllyMnBjsWBZ+0J0ghtbBDA=

+ 6 - 4
.vscode/launch.json

@@ -130,10 +130,12 @@
             "request": "launch",
             "reAttach": true,
             "url": "http://localhost:1338/localDev/index.html",
-            "pathMappings": [{
-                "url": "http://localhost:1338",
-                "path": "${workspaceFolder}"
-            }],
+            "pathMappings": [
+                {
+                    "url": "http://localhost:1338",
+                    "path": "${workspaceFolder}"
+                }
+            ],
             "preLaunchTask": "run"
         },
         {

文件差异内容过多而无法显示
+ 4878 - 4921
Playground/babylon.d.txt


+ 6 - 12
Tools/Gulp/config.json

@@ -14,7 +14,9 @@
         ],
         "outputCustomConfigurationsDirectory": "../../dist/preview release/customConfigurations",
         "srcOutputDirectory": "../../src/",
-        "currentConfig": "all"
+        "currentConfig": "all",
+        "typedocJSON": "../../.temp/babylon.typedoc.json",
+        "typedocValidationBaseline": "../../dist/preview release/typedocValidationBaseline.json" 
     },
     "buildConfigurations": {
         "all": [
@@ -67,7 +69,6 @@
             "debug",
             "morphTargets",
             "octrees",
-            "simd",
             "vr",
             "virtualJoystick",
             "optimizations",
@@ -141,7 +142,6 @@
             "debug",
             "morphTargets",
             "octrees",
-            "simd",
             "vr",
             "virtualJoystick",
             "optimizations",
@@ -192,6 +192,7 @@
                 "../../src/Rendering/babylon.renderingManager.js",
                 "../../src/Rendering/babylon.renderingGroup.js",
                 "../../src/babylon.scene.js",
+                "../../src/babylon.assetContainer.js",
                 "../../src/Mesh/babylon.buffer.js",
                 "../../src/Mesh/babylon.vertexBuffer.js",
                 "../../src/Materials/Textures/babylon.internalTexture.js",
@@ -641,7 +642,8 @@
                 "../../src/Gamepad/Controllers/babylon.oculusTouchController.js",
                 "../../src/Gamepad/Controllers/babylon.viveController.js",
                 "../../src/Gamepad/Controllers/babylon.genericController.js",
-                "../../src/Gamepad/Controllers/babylon.windowsMotionController.js"
+                "../../src/Gamepad/Controllers/babylon.windowsMotionController.js",
+                "../../src/Gamepad/Controllers/babylon.gearVRController.js"
             ],
             "dependUpon": [
                 "core"
@@ -937,14 +939,6 @@
                 "core"
             ]
         },
-        "simd": {
-            "files": [
-                "../../src/Math/babylon.math.SIMD.js"
-            ],
-            "dependUpon": [
-                "core"
-            ]
-        },
         "vr": {
             "files": [
                 "../../src/PostProcess/babylon.vrDistortionCorrectionPostProcess.js",

+ 711 - 0
Tools/Gulp/gulp-validateTypedoc.js

@@ -0,0 +1,711 @@
+'use strict';
+
+var fs = require('fs');
+var Vinyl = require('vinyl');
+var path = require('path');
+var through = require('through2');
+var PluginError = require('plugin-error');
+var supportsColor = require('color-support');
+
+// ______________________________________________ LOGS ______________________________________________
+
+var hasColors = supportsColor();
+
+var red =       hasColors ? '\x1b[31m' : '';
+var yellow =    hasColors ? '\x1b[33m' : '';
+var green =     hasColors ? '\x1b[32m' : '';
+var gray =      hasColors ? '\x1b[90m' : '';
+var white =     hasColors ? '\x1b[97m' : '';
+var clear =     hasColors ? '\x1b[0m' : '';
+
+var currentColor = undefined;
+
+function getTimestamp() {
+    var time = new Date();
+    var timeInString = ("0" + time.getHours()).slice(-2) + ":" + 
+        ("0" + time.getMinutes()).slice(-2) + ":" + 
+        ("0" + time.getSeconds()).slice(-2);
+
+    if (currentColor) {
+        return white + '[' + currentColor + timeInString + clear + white + ']';
+    }
+    else {
+        return white + '[' + gray + timeInString + white + ']';
+    }
+}
+
+function log() {
+    currentColor = gray;
+    var time = getTimestamp();
+    process.stdout.write(time + ' ');
+    currentColor = undefined;
+
+    console.log.apply(console, arguments);
+    return this;
+}
+
+function warn() {
+    currentColor = yellow;
+    var time = getTimestamp();
+    process.stdout.write(time + ' ');
+    currentColor = undefined;
+
+    console.warn.apply(console, arguments);
+    return this;
+}
+
+function err() {
+    currentColor = red;
+    var time = getTimestamp();
+    process.stderr.write(time + ' ');
+    currentColor = undefined;
+    
+    console.error.apply(console, arguments);
+    return this;
+}
+
+function success() {
+    currentColor = green;
+    var time = getTimestamp();
+    process.stdout.write(time + ' ');
+    currentColor = undefined;
+
+    console.log.apply(console, arguments);
+    return this;
+}
+
+// ______________________________________________ VALIDATION ____________________________________________
+
+function unixStylePath(filePath) {
+    return filePath.replace(/\\/g, '/');
+}
+
+function Validate(validationBaselineFileName, namespaceName, validateNamingConvention, generateBaseLine) {
+    this.validationBaselineFileName = validationBaselineFileName;
+    this.namespaceName = namespaceName;
+    this.validateNamingConvention = validateNamingConvention;
+    this.generateBaseLine = generateBaseLine;
+
+    this.previousResults = { };
+    this.results = {
+        errors: 0
+    };
+}
+
+Validate.hasTag = function(node, tagName) {
+    tagName = tagName.trim().toLowerCase();
+
+    if (node.comment && node.comment.tags) {
+        for (var i = 0; i < node.comment.tags.length; i++) {
+            if (node.comment.tags[i].tag === tagName) {
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+Validate.position = function(node) {
+    if (!node.sources) {
+        log(node);
+    }
+    return node.sources[0].fileName + ':' + node.sources[0].line;
+}
+
+Validate.upperCase = new RegExp("^[A-Z_]*$");
+Validate.pascalCase = new RegExp("^[A-Z][a-zA-Z0-9_]*$");
+Validate.camelCase = new RegExp("^[a-z][a-zA-Z0-9_]*$");
+Validate.underscoreCamelCase = new RegExp("^_[a-z][a-zA-Z0-9_]*$");
+Validate.underscorePascalCase = new RegExp("^_[A-Z][a-zA-Z0-9_]*$");
+
+Validate.prototype.errorCallback = function (parent, node, nodeKind, category, type, msg, position) {
+    this.results[this.filePath] = this.results[this.filePath] || { errors: 0 };
+    var results = this.results[this.filePath];
+
+    if (node === "toString") {
+        node = "ToString";
+    }
+    
+    // Checks against previous results.
+    var previousResults = this.previousResults[this.filePath];
+    if (previousResults) {
+        var previousRootName = parent ? parent : node;
+        var needCheck = true; 
+
+        if (Array.isArray(previousRootName)) {
+            while (previousRootName.length > 1) {
+                var previousFirst = previousRootName.shift();
+                previousResults = previousResults[previousFirst];
+                if (!previousResults) {
+                    needCheck = false;
+                    break;
+                }
+            }
+            previousRootName = previousRootName.shift();
+        }
+
+        if (needCheck) {
+            var previousNode = previousResults[previousRootName];
+            if (previousNode) {
+                var previousNodeKind = previousNode[nodeKind];
+                if (previousNodeKind) {
+
+                    if (parent) {
+                        previousNode = previousNodeKind[node];
+                    }
+                    else {
+                        previousNode = previousNodeKind;
+                    }
+
+                    if (previousNode) {
+                        var previousCategory = previousNode[category];
+                        if (previousCategory) {
+                            var previousType = previousCategory[type];
+                            if (previousType) {
+                                // Early exit as it was already in the previous build.
+                                return;
+                            }    
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // Write Error in output JSON.
+    var rootName = parent ? parent : node;
+    var current = results;
+    if (Array.isArray(rootName)) {
+        while (rootName.length > 1) {
+            var first = rootName.shift();
+            current = current[first] = current[first] || { };
+        }
+        rootName = rootName.shift();
+    }
+
+    current = current[rootName] = current[rootName] || { };
+    current = current[nodeKind] = current[nodeKind] || { };    
+    if (parent) {
+        current = current[node] = current[node] || { };
+    }
+    current = current[category] = current[category] || { };
+    current = current[type] = true;
+    
+    results.errors++;
+
+    if (!this.generateBaseLine) {
+        err(msg, position);
+    }
+}
+
+Validate.prototype.init = function (cb) {
+    var self = this;
+    if (!this.generateBaseLine && fs.existsSync(this.validationBaselineFileName)) {
+        fs.readFile(this.validationBaselineFileName, "utf-8", function (err, data) {
+            self.previousResults = JSON.parse(data);
+            cb();
+        });
+    }
+    else {
+        cb();
+    }
+}
+
+Validate.prototype.add = function (filePath, content) {
+    this.filePath = filePath && unixStylePath(filePath);
+
+    if (!Buffer.isBuffer(content)) {
+        content = new Buffer(content);
+    }
+
+    var contentString = content.toString();
+    var json = JSON.parse(contentString);
+
+    this.validateTypedoc(json);
+    this.results.errors += this.results[this.filePath].errors;
+}
+
+Validate.prototype.getResults = function () {
+    return this.results;
+}
+
+Validate.prototype.getContents = function () {
+    return Buffer.from(JSON.stringify(this.results));
+}
+
+/**
+ * Validate a TypeDoc JSON file
+ */
+Validate.prototype.validateTypedoc = function (json) {
+    var namespaces = json.children[0].children;
+    var namespace = null;
+
+    var containerNode;
+    var childNode;
+    var children;
+    var signatures;
+    var signatureNode;
+    var tags;
+    var isPublic;
+
+    // Check for BABYLON namespace
+    for (var child in namespaces) {
+        if (namespaces[child].name === this.namespaceName) {
+            namespace = namespaces[child];
+            break;
+        }
+    }
+
+    // Exit if not BABYLON related.
+    if (!namespace || !namespace.children) {
+        return;
+    }
+
+    // Validate Classes
+    for (var a in namespace.children) {
+        containerNode = namespace.children[a];
+
+        // If comment contains @ignore then skip validation completely
+        if (Validate.hasTag(containerNode, 'ignore')) continue;
+
+        // Account for undefined access modifiers.
+        if (!containerNode.flags.isPublic &&
+            !containerNode.flags.isPrivate &&
+            !containerNode.flags.isProtected) {
+                containerNode.flags.isPublic = true;
+        }
+        isPublic = containerNode.flags.isPublic;
+
+        // Validate naming.
+        this.validateNaming(null, containerNode);
+
+        // Validate Comments.
+        if (isPublic && !this.validateComment(containerNode)) {            
+            this.errorCallback(null,
+                containerNode.name,
+                containerNode.kindString,
+                "Comments",
+                "MissingText",
+                "Missing text for " + containerNode.kindString + " : " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(containerNode));
+        }
+
+        //if comment contains tag @ignoreChildren, then don't validate children
+        var validateChildren = !Validate.hasTag(containerNode, 'ignoreChildren');
+        children = containerNode.children;
+
+        //Validate Properties
+        if (validateChildren && children) {
+            for (var b in children) {
+                childNode = children[b];
+
+                // Account for undefined access modifiers.
+                if (!childNode.flags.isPublic &&
+                    !childNode.flags.isPrivate &&
+                    !childNode.flags.isProtected) {
+                    childNode.flags.isPublic = true;
+                }
+                isPublic = childNode.flags.isPublic;
+
+                // Validate Naming.
+                this.validateNaming(containerNode, childNode);
+
+                //if comment contains @ignore then skip validation completely
+                if (Validate.hasTag(childNode, 'ignore')) continue;                
+
+                if (isPublic) {
+                    tags = this.validateTags(childNode);
+                    if (tags) {
+                        this.errorCallback(containerNode.name,
+                            childNode.name,
+                            childNode.kindString,
+                            "Tags",
+                            tags,
+                            "Unrecognized tag " + tags + " at " + childNode.name + " (id: " + childNode.id + ") in " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(childNode));
+                    }
+                }
+
+                if (!this.validateComment(childNode)) {
+                    //Validate Signatures
+                    signatures = childNode.signatures;
+                    if (signatures) {
+                        for (var c in signatures) {
+                            signatureNode = signatures[c];
+
+                            //if node contains @ignore then skip validation completely
+                            if (Validate.hasTag(signatureNode, 'ignore')) continue;
+
+                            if (isPublic) {
+                                if (!this.validateComment(signatureNode)) {
+                                    this.errorCallback(containerNode.name,
+                                        signatureNode.name,
+                                        childNode.kindString,
+                                        "Comments",
+                                        "MissingText",
+                                        "Missing text for " + childNode.kindString + " : " + signatureNode.name + " (id: " + signatureNode.id + ") in " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(childNode));
+                                }
+
+                                tags = this.validateTags(signatureNode);
+                                if (tags) {
+                                    this.errorCallback(containerNode.name,
+                                        signatureNode.name,
+                                        childNode.kindString,
+                                        "Tags",
+                                        tags,
+                                        "Unrecognized tag " + tags + " at " + signatureNode.name + " (id: " + signatureNode.id + ") in " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(childNode));
+                                }
+
+                                if (signatureNode.type.name !== "void" && signatureNode.comment && !signatureNode.comment.returns) {
+                                    this.errorCallback(containerNode.name,
+                                        signatureNode.name,
+                                        childNode.kindString,
+                                        "Comments",
+                                        "MissingReturn",
+                                        "No Return Comment at " + signatureNode.name + " (id: " + signatureNode.id + ") in " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(childNode));
+                                }
+
+                                if (signatureNode.type.name === "void" && signatureNode.comment && signatureNode.comment.returns) {
+                                    this.errorCallback(containerNode.name,
+                                        signatureNode.name,
+                                        childNode.kindString,
+                                        "Comments",
+                                        "UselessReturn",
+                                        "No Return Comment Needed at " + signatureNode.name + " (id: " + signatureNode.id + ") in " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(childNode));
+                                }
+                            }
+
+                            this.validateParameters(containerNode, childNode, signatureNode, signatureNode.parameters, isPublic);
+                        }
+                    } else {
+                        this.errorCallback(containerNode.name,
+                            childNode.name,
+                            childNode.kindString,
+                            "Comments",
+                            "MissingText",
+                            "Missing text for " + childNode.kindString + " : " + childNode.name + " (id: " + childNode.id + ") in " + containerNode.name + " (id: " + containerNode.id + ")", Validate.position(childNode));
+                    }
+                }
+
+                // this.validateParameters(containerNode, childNode, childNode.parameters, isPublic);
+            }
+        }
+    }
+}
+
+/**
+ * Validate that tags are recognized
+ */
+Validate.prototype.validateTags = function(node) {
+    var tags;
+    var errorTags = [];
+
+    if (node.comment) {
+
+        tags = node.comment.tags;
+        if (tags) {
+            for (var i = 0; i < tags.length; i++) {
+                var tag = tags[i];
+                var validTags = ["constructor", "throw", "type", "deprecated", "example", "examples", "remark", "see", "remarks"]
+                if (validTags.indexOf(tag.tag) === -1) {
+                    errorTags.push(tag.tag);
+                }
+            }
+        }
+
+    }
+
+    return errorTags.join(",");
+}
+
+/**
+ * Validate that a JSON node has the correct TypeDoc comments
+ */
+Validate.prototype.validateComment = function(node) {
+
+    // Return-only methods are allowed to just have a @return tag
+    if ((node.kindString === "Call signature" || node.kindString === "Accessor") && !node.parameters && node.comment && node.comment.returns) {
+        return true;
+    }
+
+    // Return true for private properties (dont validate)
+    if ((node.kindString === "Property" || node.kindString === "Object literal") && (node.flags.isPrivate || node.flags.isProtected)) {
+        return true;
+    }
+
+    // Return true for inherited properties
+    if (node.inheritedFrom) {
+        return true;
+    }
+
+    if (node.comment) {
+
+        if (node.comment.text || node.comment.shortText) {
+            return true;
+        }
+
+        return false;
+    }
+
+    return false;
+}
+
+/**
+ * Validate comments for paramters on a node
+ */
+Validate.prototype.validateParameters = function(containerNode, method, signature, parameters, isPublic) {
+    var parametersNode;
+    for (var parameter in parameters) {
+        parametersNode = parameters[parameter];
+
+        if (isPublic && !this.validateComment(parametersNode)) {
+            // throw containerNode.name + " " + method.kindString + " " + method.name + " " + parametersNode.name + " " + parametersNode.kindString;
+            this.errorCallback([containerNode.name, method.kindString, signature.name],
+                parametersNode.name,
+                parametersNode.kindString,
+                "Comments",
+                "MissingText",
+                "Missing text for parameter " + parametersNode.name + " (id: " + parametersNode.id + ") of " + method.name + " (id: " + method.id + ")", Validate.position(method));
+        }
+
+        if (this.validateNamingConvention && !Validate.camelCase.test(parametersNode.name)) {
+            this.errorCallback([containerNode.name, method.kindString, signature.name],
+                parametersNode.name,
+                parametersNode.kindString,
+                "Naming",
+                "NotCamelCase",
+                "Parameter " + parametersNode.name + " should be Camel Case (id: " + method.id + ")", Validate.position(method));
+        }
+    }
+}
+
+/**
+ * Validate naming conventions of a node
+ */
+Validate.prototype.validateNaming = function(parent, node) {
+    if (!this.validateNamingConvention) {
+        return;
+    }
+
+    if (node.inheritedFrom) {
+        return;
+    }
+
+    // Internals are not subject to the public visibility policy.
+    if (node.name && node.name.length > 0 && node.name[0] === "_") {
+        return;
+    }
+
+    if ((node.flags.isPrivate || node.flags.isProtected) && node.flags.isStatic) {
+        if (!Validate.underscorePascalCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotUnderscorePascalCase",
+                node.name + " should be Underscore Pascal Case (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.flags.isPrivate || node.flags.isProtected) {
+        if (!Validate.underscoreCamelCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotUnderscoreCamelCase",
+                node.name + " should be Underscore Camel Case (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.flags.isStatic) {
+        if (!Validate.pascalCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotPascalCase",
+                node.name + " should be Pascal Case (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.kindString == "Module") {
+        if (!Validate.upperCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotUpperCase",
+                "Module is not Upper Case " + node.name + " (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.kindString == "Interface" ||
+        node.kindString == "Class" ||
+        node.kindString == "Enumeration" ||
+        node.kindString == "Enumeration member" ||
+        node.kindString == "Accessor" ||
+        node.kindString == "Type alias") {
+        if (!Validate.pascalCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotPascalCase",
+                node.name + " should be Pascal Case (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.kindString == "Method" ||
+        node.kindString == "Property" ||
+        node.kindString == "Object literal") {
+
+        // Only warn here as special properties such as FOV may be better capitalized 
+        if (!Validate.camelCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotCamelCase",
+                node.name + " should be Camel Case (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.kindString == "Variable") {
+        this.errorCallback(parent ? parent.name : null,
+            node.name,
+            node.kindString,
+            "Naming",
+            "ShouldNotBeLooseVariable",
+            node.name + " should not be a variable (id: " + node.id + ")", Validate.position(node));
+    }
+    else if (node.kindString === "Function") {
+        if (!Validate.camelCase.test(node.name)) {
+            this.errorCallback(parent ? parent.name : null,
+                node.name,
+                node.kindString,
+                "Naming",
+                "NotCamelCase",
+                node.name + " should be Camel Case (id: " + node.id + ")", Validate.position(node));
+        }
+    }
+    else if (node.kindString == "Constructor") {
+        // Do Nothing Here, this is handled through the class name.
+    }
+    else {
+        this.errorCallback(parent ? parent.name : null,
+            node.name,
+            node.kindString,
+            "Naming",
+            "UnknownNamingConvention",
+            "Unknown naming convention for " + node.kindString + " at " + node.name + " (id: " + node.id + ")", Validate.position(node));
+    }
+}
+
+// ______________________________________________ PLUGIN ____________________________________________
+
+// consts
+const PLUGIN_NAME = 'gulp-validateTypedoc';
+
+// plugin level function (dealing with files)
+function gulpValidateTypedoc(validationBaselineFileName, namespaceName, validateNamingConvention, generateBaseLine) {
+
+    if (!validationBaselineFileName) {
+        throw new PluginError(PLUGIN_NAME, 'Missing validation filename!');
+    }
+    if (typeof validationBaselineFileName !== "string") {
+        throw new PluginError(PLUGIN_NAME, 'Validation filename must be a string!');
+    }
+
+    var validate;
+    var latestFile;
+
+    function bufferContents(file, enc, cb) {
+        // ignore empty files
+        if (file.isNull()) {
+            cb();
+            return;
+        }
+
+        // we don't do streams (yet)
+        if (file.isStream()) {
+            this.emit('error', new Error('gulp-validatTypedoc: Streaming not supported'));
+            cb();
+            return;
+        }
+
+        // set latest file if not already set,
+        // or if the current file was modified more recently.
+        latestFile = file;
+
+        // What will happen once all set.
+        var done = function () {
+            // add file to concat instance
+            validate.add(file.relative, file.contents);
+
+            cb();
+        }
+
+        // Do the validation.
+        if (!validate) {
+            validate = new Validate(validationBaselineFileName, namespaceName, validateNamingConvention, generateBaseLine);
+            validate.init(done);
+        }
+        else {
+            done();
+        }
+    }
+
+    function endStream(cb) {
+        // no files passed in, no file goes out
+        if (!latestFile) {
+            var error = new PluginError(PLUGIN_NAME, 'gulp-validatTypedoc: No Baseline found.');
+            this.emit('error', error);
+            cb();
+            return;
+        }
+
+        var results = validate.getResults();
+        var buffer = Buffer.from(JSON.stringify(results, null, 2))
+
+        if (generateBaseLine) {
+            fs.writeFileSync(validationBaselineFileName, buffer || '');
+        }
+
+        var jsFile = new Vinyl({
+            cwd: process.cwd,
+            base: null,
+            path: validationBaselineFileName,
+            contents: buffer
+        });
+
+        this.push(jsFile);
+
+        var action = generateBaseLine ? "baseline generation" : "validation";
+        var self = this;
+        var error = function(message) {
+            generateBaseLine ? warn : err;
+            if (generateBaseLine) {
+                warn(message);
+            }
+            else {
+                err(message);
+                var error = new PluginError(PLUGIN_NAME, message);
+                self.emit('error', error);
+            }
+        }
+
+        if (results.errors > 1) {
+            var message = results.errors + " errors have been detected during the " + action + " !";
+            error(message);
+        }
+        else if (results.errors === 1) {
+            var message = "1 error has been detected during the " + action + " !";
+            error(message);
+        }
+        else {
+            var message = "All formatting check passed successfully during the " + action + " !";
+            success(message);
+        }
+
+        cb();
+    }
+
+    return through.obj(bufferContents, endStream);
+};
+
+// exporting the plugin main function
+module.exports = gulpValidateTypedoc;

+ 109 - 28
Tools/Gulp/gulpfile.js

@@ -25,6 +25,8 @@ var webserver = require("gulp-webserver");
 var path = require("path");
 var sass = require("gulp-sass");
 var webpack = require("webpack-stream");
+var typedoc = require("gulp-typedoc");
+var validateTypedoc = require("./gulp-validateTypedoc");
 
 var config = require("./config.json");
 
@@ -435,8 +437,8 @@ var buildExternalLibrary = function (library, settings, watch) {
  * The default task, concat and min the main BJS files.
  */
 gulp.task("default", function (cb) {
-    // runSequence("typescript-all", "intellisense", "tests-browserStack", cb);
-    runSequence("typescript-all", "intellisense", cb);
+    runSequence("typescript-all", "intellisense", "typedoc-all", "tests-validation-virtualscreen", "tests-validation-browserstack", cb);
+    // runSequence("typescript-all", "intellisense", "typedoc-all", "tests-validation-virtualscreen", cb);
 });
 
 gulp.task("mainBuild", function (cb) {
@@ -575,32 +577,9 @@ gulp.task("webserver", function () {
 gulp.task("run", ["watch", "webserver"], function () {
 });
 
-
-gulp.task("tests-integration", function (done) {
-    var kamaServerOptions = {
-        configFile: __dirname + "/../../tests/validation/karma.conf.js",
-        singleRun: false
-    };
-
-    var server = new karmaServer(kamaServerOptions, done);
-    server.start();
-});
-
-gulp.task("tests-browserStack", function (done) {
-    if (!process.env.TRAVIS) {
-        done();
-        return;
-    }
-
-    var kamaServerOptions = {
-        configFile: __dirname + "/../../tests/validation/karma.conf.browserstack.js",
-        singleRun: true
-    };
-
-    var server = new karmaServer(kamaServerOptions, done);
-    server.start();
-});
-
+/**
+ * Cleans map and js files from the src folder.
+ */
 gulp.task("clean-JS-MAP", function () {
     return del([
         "../../src/**/*.js.map", "../../src/**/*.js"
@@ -809,3 +788,105 @@ gulp.task("modules", ["prepare-dependency-tree"], function () {
     // run da tasks man!
     return merge2(tasks);
 })
+
+/**
+ * Generate the TypeDoc JSON output in order to create code metadata.
+ */
+gulp.task("typedoc-generate", function () {
+    return gulp
+        .src(["../../dist/preview release/babylon.d.ts"])
+        .pipe(typedoc({
+            // TypeScript options (see typescript docs)
+            mode: "modules",
+            module: "commonjs",
+            target: "es5",
+            includeDeclarations: true,
+ 
+            // Output options (see typedoc docs)
+            json: config.build.typedocJSON,
+ 
+            // TypeDoc options (see typedoc docs)
+            ignoreCompilerErrors: true,
+
+            readme: "none",
+
+            excludeExternals: true,
+            excludePrivate: true,
+            excludeProtected: true,
+
+            entryPoint: ["\"babylon.d\"", "BABYLON"]
+        }));
+});
+
+/**
+ * Validate the TypeDoc JSON output against the current baselin to ensure our code is correctly documented.
+ * (in the newly introduced areas)
+ */
+gulp.task("typedoc-validate", function () {
+    return gulp.src(config.build.typedocJSON)
+        .pipe(validateTypedoc(config.build.typedocValidationBaseline, "BABYLON", true, false));
+});
+
+/**
+ * Generate the validation reference to ensure our code is correctly documented.
+ */
+gulp.task("typedoc-generateValidationBaseline", function () {
+    return gulp.src(config.build.typedocJSON)
+    .pipe(validateTypedoc(config.build.typedocValidationBaseline, "BABYLON", true, true));
+});
+
+/**
+ * Validate the code comments and style case convention through typedoc and
+ * generate the new baseline.
+ */
+gulp.task("typedoc-all", function (cb) {
+    runSequence("typedoc-generate", "typedoc-validate", "typedoc-generateValidationBaseline", cb);
+});
+
+/**
+ * Launches the KARMA validation tests in chrome in order to debug them.
+ * (Can only be launch locally.)
+ */
+gulp.task("tests-validation-karma", function (done) {
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../tests/validation/karma.conf.js",
+        singleRun: false
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});
+
+/**
+ * Launches the KARMA validation tests in ff or virtual screen ff on travis for a quick analysis during the build.
+ * (Can only be launch on any branches.)
+ */
+gulp.task("tests-validation-virtualscreen", function (done) {
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../tests/validation/karma.conf.js",
+        singleRun: true,
+        browsers: ['Firefox']
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});
+
+/**
+ * Launches the KARMA validation tests in browser stack for remote and cross devices validation tests.
+ * (Can only be launch from secure branches.)
+ */
+gulp.task("tests-validation-browserstack", function (done) {
+    if (!process.env.BROWSER_STACK_USERNAME) {
+        done();
+        return;
+    }
+
+    var kamaServerOptions = {
+        configFile: __dirname + "/../../tests/validation/karma.conf.browserstack.js",
+        singleRun: true
+    };
+
+    var server = new karmaServer(kamaServerOptions, done);
+    server.start();
+});

+ 15 - 11
Tools/Gulp/package.json

@@ -11,6 +11,8 @@
     "devDependencies": {
         "@types/node": "^8.5.0",
         "base64-image-loader": "^1.2.1",
+        "chai": "^4.1.2",
+        "color-support": "^1.1.3",
         "css-loader": "^0.25.0",
         "deepmerge": "^2.0.1",
         "del": "2.2.2",
@@ -28,6 +30,7 @@
         "gulp-replace": "~0.5.3",
         "gulp-sass": "3.1.0",
         "gulp-sourcemaps": "~1.9.1",
+        "gulp-typedoc": "^2.1.2",
         "gulp-typescript": "^3.2.3",
         "gulp-uglify": "^2.1.2",
         "gulp-util": "~3.0.4",
@@ -36,25 +39,26 @@
         "html-loader": "^0.5.1",
         "imports-loader": "^0.7.1",
         "json-loader": "^0.5.7",
+        "karma": "^2.0.0",
+        "karma-browserstack-launcher": "^1.3.0",
+        "karma-chai": "^0.1.0",
+        "karma-chrome-launcher": "^2.2.0",
+        "karma-firefox-launcher": "^1.1.0",
+        "karma-mocha": "^1.3.0",
+        "karma-sinon": "^1.0.5",
         "merge2": "~0.3.5",
         "minimist": "^1.2.0",
+        "mocha": "^4.0.1",
         "run-sequence": "~1.1.0",
+        "sinon": "^4.1.3",
         "style-loader": "^0.13.2",
         "through2": "~0.6.5",
         "ts-loader": "^2.3.7",
+        "typedoc": "^0.9.0",
         "typescript": "^2.6.2",
-        "webpack-stream": "^4.0.0",
-        "karma": "^2.0.0",
-        "karma-chrome-launcher": "^2.2.0",
-        "karma-browserstack-launcher": "^1.3.0",
-        "mocha": "^4.0.1",
-        "chai": "^4.1.2",
-        "sinon": "^4.1.3",
-        "karma-mocha": "^1.3.0",
-        "karma-chai": "^0.1.0",
-        "karma-sinon": "^1.0.5"
+        "webpack-stream": "^4.0.0"
     },
     "scripts": {
-        "install": "npm --prefix ../../Playground/ install ../../Playground/ && gulp typescript-compile && gulp typescript-libraries && gulp deployLocalDev"
+        "install": "npm --prefix ../../Playground/ install ../../Playground/ && gulp deployLocalDev"
     }
 }

文件差异内容过多而无法显示
+ 2888 - 2931
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 49 - 49
dist/preview release/babylon.js


文件差异内容过多而无法显示
+ 1410 - 709
dist/preview release/babylon.max.js


文件差异内容过多而无法显示
+ 51 - 51
dist/preview release/babylon.worker.js


文件差异内容过多而无法显示
+ 3516 - 3559
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


文件差异内容过多而无法显示
+ 53 - 53
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


文件差异内容过多而无法显示
+ 1440 - 709
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


+ 2 - 2
dist/preview release/gui/babylon.gui.js

@@ -2820,10 +2820,10 @@ var BABYLON;
                     }
                     context.fillStyle = this.color;
                     if (this.isThumbClamped) {
-                        context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width + effectiveThumbWidth, this._currentMeasure.height - effectiveBarOffset * 2);
+                        context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
                     }
                     else {
-                        context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+                        context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
                     }
                     if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
                         context.shadowColor = this.shadowColor;

文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


文件差异内容过多而无法显示
+ 4 - 4
dist/preview release/inspector/babylon.inspector.bundle.js


文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -116,6 +116,7 @@ declare module BABYLON {
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAssetsAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (assets: AssetContainer) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;

+ 22 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -194,6 +194,28 @@ var BABYLON;
                 }
             }
         };
+        GLTFFileLoader.prototype.loadAssetsAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
+            try {
+                var loaderData = this._parse(data);
+                this._loader = this._getLoader(loaderData);
+                this._loader.importMeshAsync(null, scene, loaderData, rootUrl, function (meshes, particleSystems, skeletons) {
+                    var container = new BABYLON.AssetContainer(scene);
+                    Array.prototype.push.apply(container.meshes, meshes);
+                    Array.prototype.push.apply(container.particleSystems, particleSystems);
+                    Array.prototype.push.apply(container.skeletons, skeletons);
+                    container.removeAllFromScene();
+                    onSuccess(container);
+                }, onProgress, onError);
+            }
+            catch (e) {
+                if (onError) {
+                    onError(e.message, e);
+                }
+                else {
+                    BABYLON.Tools.Error(e.message);
+                }
+            }
+        };
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -116,6 +116,7 @@ declare module BABYLON {
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAssetsAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (assets: AssetContainer) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;

+ 30 - 0
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -194,6 +194,28 @@ var BABYLON;
                 }
             }
         };
+        GLTFFileLoader.prototype.loadAssetsAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
+            try {
+                var loaderData = this._parse(data);
+                this._loader = this._getLoader(loaderData);
+                this._loader.importMeshAsync(null, scene, loaderData, rootUrl, function (meshes, particleSystems, skeletons) {
+                    var container = new BABYLON.AssetContainer(scene);
+                    Array.prototype.push.apply(container.meshes, meshes);
+                    Array.prototype.push.apply(container.particleSystems, particleSystems);
+                    Array.prototype.push.apply(container.skeletons, skeletons);
+                    container.removeAllFromScene();
+                    onSuccess(container);
+                }, onProgress, onError);
+            }
+            catch (e) {
+                if (onError) {
+                    onError(e.message, e);
+                }
+                else {
+                    BABYLON.Tools.Error(e.message);
+                }
+            }
+        };
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
@@ -1375,6 +1397,14 @@ var BABYLON;
                     sampler.interpolation = sampler.interpolation || "LINEAR";
                     var getNextKey;
                     switch (sampler.interpolation) {
+                        case "STEP": {
+                            getNextKey = function (frameIndex) { return ({
+                                frame: inputData[frameIndex],
+                                value: getNextOutputValue(),
+                                interpolation: BABYLON.AnimationKeyInterpolation.STEP
+                            }); };
+                            break;
+                        }
                         case "LINEAR": {
                             getNextKey = function (frameIndex) { return ({
                                 frame: inputData[frameIndex],

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -116,6 +116,7 @@ declare module BABYLON {
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAssetsAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (assets: AssetContainer) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;

+ 30 - 0
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -194,6 +194,28 @@ var BABYLON;
                 }
             }
         };
+        GLTFFileLoader.prototype.loadAssetsAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
+            try {
+                var loaderData = this._parse(data);
+                this._loader = this._getLoader(loaderData);
+                this._loader.importMeshAsync(null, scene, loaderData, rootUrl, function (meshes, particleSystems, skeletons) {
+                    var container = new BABYLON.AssetContainer(scene);
+                    Array.prototype.push.apply(container.meshes, meshes);
+                    Array.prototype.push.apply(container.particleSystems, particleSystems);
+                    Array.prototype.push.apply(container.skeletons, skeletons);
+                    container.removeAllFromScene();
+                    onSuccess(container);
+                }, onProgress, onError);
+            }
+            catch (e) {
+                if (onError) {
+                    onError(e.message, e);
+                }
+                else {
+                    BABYLON.Tools.Error(e.message);
+                }
+            }
+        };
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
@@ -3541,6 +3563,14 @@ var BABYLON;
                     sampler.interpolation = sampler.interpolation || "LINEAR";
                     var getNextKey;
                     switch (sampler.interpolation) {
+                        case "STEP": {
+                            getNextKey = function (frameIndex) { return ({
+                                frame: inputData[frameIndex],
+                                value: getNextOutputValue(),
+                                interpolation: BABYLON.AnimationKeyInterpolation.STEP
+                            }); };
+                            break;
+                        }
                         case "LINEAR": {
                             getNextKey = function (frameIndex) { return ({
                                 frame: inputData[frameIndex],

文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.objFileLoader.d.ts

@@ -58,6 +58,7 @@ declare module BABYLON {
         private _loadMTL(url, rootUrl, onSuccess);
         importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
         load(scene: Scene, data: string, rootUrl: string): boolean;
+        loadAssets(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): Nullable<AssetContainer>;
         /**
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.

+ 9 - 0
dist/preview release/loaders/babylon.objFileLoader.js

@@ -260,6 +260,15 @@ var BABYLON;
             //Get the 3D model
             return this.importMesh(null, scene, data, rootUrl, null, null, null);
         };
+        OBJFileLoader.prototype.loadAssets = function (scene, data, rootUrl, onError) {
+            var container = new BABYLON.AssetContainer(scene);
+            var result = this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
+            if (result) {
+                container.removeAllFromScene();
+                return container;
+            }
+            return null;
+        };
         /**
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 1 - 0
dist/preview release/loaders/babylon.stlFileLoader.d.ts

@@ -9,6 +9,7 @@ declare module BABYLON {
         extensions: ISceneLoaderPluginExtensions;
         importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
         load(scene: Scene, data: any, rootUrl: string): boolean;
+        loadAssets(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): Nullable<AssetContainer>;
         private isBinary(data);
         private parseBinary(mesh, data);
         private parseASCII(mesh, solidData);

+ 9 - 0
dist/preview release/loaders/babylon.stlFileLoader.js

@@ -70,6 +70,15 @@ var BABYLON;
             }
             return result;
         };
+        STLFileLoader.prototype.loadAssets = function (scene, data, rootUrl, onError) {
+            var container = new BABYLON.AssetContainer(scene);
+            var result = this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
+            if (result) {
+                container.removeAllFromScene();
+                return container;
+            }
+            return null;
+        };
         STLFileLoader.prototype.isBinary = function (data) {
             // check if file size is correct for binary stl
             var faceSize, nFaces, reader;

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/loaders/babylon.stlFileLoader.min.js


+ 48 - 0
dist/preview release/loaders/babylonjs.loaders.js

@@ -89,6 +89,15 @@ var BABYLON;
             }
             return result;
         };
+        STLFileLoader.prototype.loadAssets = function (scene, data, rootUrl, onError) {
+            var container = new BABYLON.AssetContainer(scene);
+            var result = this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
+            if (result) {
+                container.removeAllFromScene();
+                return container;
+            }
+            return null;
+        };
         STLFileLoader.prototype.isBinary = function (data) {
             // check if file size is correct for binary stl
             var faceSize, nFaces, reader;
@@ -444,6 +453,15 @@ var BABYLON;
             //Get the 3D model
             return this.importMesh(null, scene, data, rootUrl, null, null, null);
         };
+        OBJFileLoader.prototype.loadAssets = function (scene, data, rootUrl, onError) {
+            var container = new BABYLON.AssetContainer(scene);
+            var result = this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
+            if (result) {
+                container.removeAllFromScene();
+                return container;
+            }
+            return null;
+        };
         /**
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
@@ -1168,6 +1186,28 @@ var BABYLON;
                 }
             }
         };
+        GLTFFileLoader.prototype.loadAssetsAsync = function (scene, data, rootUrl, onSuccess, onProgress, onError) {
+            try {
+                var loaderData = this._parse(data);
+                this._loader = this._getLoader(loaderData);
+                this._loader.importMeshAsync(null, scene, loaderData, rootUrl, function (meshes, particleSystems, skeletons) {
+                    var container = new BABYLON.AssetContainer(scene);
+                    Array.prototype.push.apply(container.meshes, meshes);
+                    Array.prototype.push.apply(container.particleSystems, particleSystems);
+                    Array.prototype.push.apply(container.skeletons, skeletons);
+                    container.removeAllFromScene();
+                    onSuccess(container);
+                }, onProgress, onError);
+            }
+            catch (e) {
+                if (onError) {
+                    onError(e.message, e);
+                }
+                else {
+                    BABYLON.Tools.Error(e.message);
+                }
+            }
+        };
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
@@ -4497,6 +4537,14 @@ var BABYLON;
                     sampler.interpolation = sampler.interpolation || "LINEAR";
                     var getNextKey;
                     switch (sampler.interpolation) {
+                        case "STEP": {
+                            getNextKey = function (frameIndex) { return ({
+                                frame: inputData[frameIndex],
+                                value: getNextOutputValue(),
+                                interpolation: BABYLON.AnimationKeyInterpolation.STEP
+                            }); };
+                            break;
+                        }
                         case "LINEAR": {
                             getNextKey = function (frameIndex) { return ({
                                 frame: inputData[frameIndex],

文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 3 - 0
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -15,6 +15,7 @@ declare module BABYLON {
         extensions: ISceneLoaderPluginExtensions;
         importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
         load(scene: Scene, data: any, rootUrl: string): boolean;
+        loadAssets(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): Nullable<AssetContainer>;
         private isBinary(data);
         private parseBinary(mesh, data);
         private parseASCII(mesh, solidData);
@@ -81,6 +82,7 @@ declare module BABYLON {
         private _loadMTL(url, rootUrl, onSuccess);
         importMesh(meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: Nullable<AbstractMesh[]>, particleSystems: Nullable<ParticleSystem[]>, skeletons: Nullable<Skeleton[]>): boolean;
         load(scene: Scene, data: string, rootUrl: string): boolean;
+        loadAssets(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): Nullable<AssetContainer>;
         /**
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.
@@ -215,6 +217,7 @@ declare module BABYLON {
         dispose(): void;
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess?: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
+        loadAssetsAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (assets: AssetContainer) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void;
         canDirectLoad(data: string): boolean;
         rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
         createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


文件差异内容过多而无法显示
+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/postProcessesLibrary/babylonjs.postProcess.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.min.js


文件差异内容过多而无法显示
+ 62374 - 0
dist/preview release/typedocValidationBaseline.json


文件差异内容过多而无法显示
+ 63 - 62
dist/preview release/viewer/babylon.viewer.js


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

@@ -5,6 +5,7 @@
 - New [AnimationGroup class](http://doc.babylonjs.com/how_to/group) to control simultaneously multiple animations with different targets ([deltakosh](https://github.com/deltakosh))
 
 ## Updates
+- Improved [SceneOptimizer](http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer) to provide better adapatability ([deltakosh](https://github.com/deltakosh))
 - New watcher configuration for VSCode. Now the task only compiles changed files ([sebavan](https://github.com/sebavan))
 - Added new draw modes to engine (points, lines, linesloop, linestrip, trianglestrip, trianglefan) ([benaadams](https://github.com/benaadams))
 - Added GUI Textblock.lineSpacing setter and getter to configure vertical space between lines in pixels or percentage values when working with text wrapping ([carloslanderas](https://github.com/carloslanderas))

+ 4 - 5
gui/src/controls/slider.ts

@@ -194,7 +194,7 @@ module BABYLON.GUI {
                 var left = this._currentMeasure.left;
                 var width = this._currentMeasure.width - effectiveThumbWidth;
                 var thumbPosition = ((this._value - this._minimum) / (this._maximum - this._minimum)) * width;
-                
+
                 context.fillStyle = this._background;
                 if (this.isThumbClamped) {
                     context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width + effectiveThumbWidth, this._currentMeasure.height - effectiveBarOffset * 2);
@@ -211,10 +211,10 @@ module BABYLON.GUI {
 
                 context.fillStyle = this.color;
                 if (this.isThumbClamped) {
-                    context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width + effectiveThumbWidth, this._currentMeasure.height - effectiveBarOffset * 2);
+                    context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
                 }
                 else {
-                    context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+                    context.fillRect(left + (effectiveThumbWidth / 2), this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
                 }
 
                 if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
@@ -238,7 +238,6 @@ module BABYLON.GUI {
                     context.strokeStyle = this._borderColor;
                     context.stroke();
                 }
-
                 else {
                     context.fillRect(left + thumbPosition, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
 
@@ -254,7 +253,7 @@ module BABYLON.GUI {
             }
             context.restore();
         }
-       
+
 
         // Events
         private _pointerIsDown = false;

+ 10 - 0
loaders/src/OBJ/babylon.objFileLoader.ts

@@ -264,6 +264,16 @@ module BABYLON {
             return this.importMesh(null, scene, data, rootUrl, null, null, null);
         }
 
+        public loadAssets(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void):Nullable<AssetContainer>{
+            var container = new AssetContainer(scene);
+            var result = this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
+            if(result){
+                container.removeAllFromScene();
+                return container;
+            }
+            return null;
+        }
+
         /**
          * Read the OBJ file and create an Array of meshes.
          * Each mesh contains all information given by the OBJ and the MTL file.

+ 10 - 0
loaders/src/STL/babylon.stlFileLoader.ts

@@ -86,6 +86,16 @@ module BABYLON {
             return result;
         }
 
+        public loadAssets(scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void):Nullable<AssetContainer>{
+            var container = new AssetContainer(scene);
+            var result = this.importMesh(null, scene, data, rootUrl, container.meshes, null, null);
+            if(result){
+                container.removeAllFromScene();
+                return container;
+            }
+            return null;
+        }
+
         private isBinary (data: any) {
 
             // check if file size is correct for binary stl

+ 10 - 2
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -1045,8 +1045,16 @@ module BABYLON.GLTF2 {
 
                 sampler.interpolation = sampler.interpolation || "LINEAR";
 
-                let getNextKey: (frameIndex: number) => any;
+                let getNextKey: (frameIndex: number) => IAnimationKey;
                 switch (sampler.interpolation) {
+                    case "STEP": {
+                        getNextKey = frameIndex => ({
+                            frame: inputData[frameIndex],
+                            value: getNextOutputValue(),
+                            interpolation: AnimationKeyInterpolation.STEP
+                        });
+                        break;
+                    }
                     case "LINEAR": {
                         getNextKey = frameIndex => ({
                             frame: inputData[frameIndex],
@@ -1068,7 +1076,7 @@ module BABYLON.GLTF2 {
                     }
                 };
 
-                let keys: Array<any>;
+                let keys: Array<IAnimationKey>;
                 if (inputData.length === 1) {
                     let key = getNextKey(0);
                     keys = [

+ 23 - 0
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -239,6 +239,29 @@ module BABYLON {
             }
         }
 
+        public loadAssetsAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: (assets:AssetContainer) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void): void {
+            try {
+                const loaderData = this._parse(data);
+                this._loader = this._getLoader(loaderData);
+                this._loader.importMeshAsync(null, scene, loaderData, rootUrl,  (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => {
+                    var container = new AssetContainer(scene);
+                    Array.prototype.push.apply(container.meshes, meshes)
+                    Array.prototype.push.apply(container.particleSystems, particleSystems)
+                    Array.prototype.push.apply(container.skeletons, skeletons)
+                    container.removeAllFromScene();
+                    onSuccess(container)
+                }, onProgress, onError);
+            }
+            catch (e) {
+                if (onError) {
+                    onError(e.message, e);
+                }
+                else {
+                    Tools.Error(e.message);
+                }
+            }
+        }
+
         public canDirectLoad(data: string): boolean {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         }

+ 2 - 2
readme.md

@@ -76,11 +76,11 @@ var createScene = function(){
     camera.attachControl(canvas, false);
     // create a basic light, aiming 0,1,0 - meaning, to the sky
     var light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0,1,0), scene);
-    // create a built-in "sphere" shape; its constructor takes 5 params: name, width, depth, subdivisions, scene
+    // create a built-in "sphere" shape; its constructor takes 6 params: name, segment, diameter, scene, updatable, sideOrientation
     var sphere = BABYLON.Mesh.CreateSphere('sphere1', 16, 2, scene);
     // move the sphere upward 1/2 of its height
     sphere.position.y = 1;
-    // create a built-in "ground" shape; its constructor takes the same 5 params as the sphere's one
+    // create a built-in "ground" shape; its constructor takes the same 6 params : name, width, height, subdivision, scene, updatable
     var ground = BABYLON.Mesh.CreateGround('ground1', 6, 6, 2, scene);
     // return the created scene
     return scene;

+ 20 - 5
src/Animations/babylon.animation.ts

@@ -81,10 +81,25 @@
         }
     }
 
+    export interface IAnimationKey {
+        frame: number;
+        value: any;
+        inTangent?: any;
+        outTangent?: any;
+        interpolation?: AnimationKeyInterpolation;
+    }
+
+    export enum AnimationKeyInterpolation {
+        /**
+         * Do not interpolate between keys and use the start key value only. Tangents are ignored.
+         */
+        STEP = 1
+    }
+
     export class Animation {
         public static AllowMatricesInterpolation = false;
 
-        private _keys: Array<{ frame: number, value: any, inTangent?: any, outTangent?: any }>;
+        private _keys: Array<IAnimationKey>;
         private _easingFunction: IEasingFunction;
 
         public _runtimeAnimations = new Array<RuntimeAnimation>();
@@ -122,7 +137,7 @@
 
             var animation = new Animation(name, targetProperty, framePerSecond, dataType, loopMode);
 
-            var keys: Array<{ frame: number, value: any }> = [{ frame: 0, value: from }, { frame: totalFrame, value: to }];
+            var keys: Array<IAnimationKey> = [{ frame: 0, value: from }, { frame: totalFrame, value: to }];
             animation.setKeys(keys);
 
             if (easingFunction !== undefined) {
@@ -370,7 +385,7 @@
         }
 
 
-        public getKeys(): Array<{ frame: number, value: any, inTangent?: any, outTangent?: any }> {
+        public getKeys(): Array<IAnimationKey> {
             return this._keys;
         }
 
@@ -461,7 +476,7 @@
             return clone;
         }
 
-        public setKeys(values: Array<{ frame: number, value: any }>): void {
+        public setKeys(values: Array<IAnimationKey>): void {
             this._keys = values.slice(0);
         }
 
@@ -573,7 +588,7 @@
             var animation = new Animation(parsedAnimation.name, parsedAnimation.property, parsedAnimation.framePerSecond, parsedAnimation.dataType, parsedAnimation.loopBehavior);
 
             var dataType = parsedAnimation.dataType;
-            var keys: Array<{ frame: number, value: any, inTangent: any, outTangent: any }> = [];
+            var keys: Array<IAnimationKey> = [];
             var data;
             var index: number;
 

+ 4 - 0
src/Animations/babylon.runtimeAnimation.ts

@@ -75,6 +75,10 @@
 
                     var startKey = keys[key];
                     var startValue = this._getKeyValue(startKey.value);
+                    if (startKey.interpolation === AnimationKeyInterpolation.STEP) {
+                        return startValue;
+                    }
+
                     var endValue = this._getKeyValue(endKey.value);
 
                     var useTangent = startKey.outTangent !== undefined && endKey.inTangent !== undefined;

+ 13 - 4
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -112,12 +112,12 @@ module BABYLON {
 
         /**
          * Observable raised when a new mesh is selected based on meshSelectionPredicate
-         */        
+         */
         public onNewMeshSelected = new Observable<AbstractMesh>();
 
         /**
          * Observable raised when a new mesh is picked based on meshSelectionPredicate
-         */        
+         */
         public onNewMeshPicked = new Observable<PickingInfo>();
 
         private _circleEase: CircleEase;
@@ -152,7 +152,7 @@ module BABYLON {
         /**
          * Set teleportation enabled. If set to false camera teleportation will be disabled but camera rotation will be kept.
          */
-        public teleportationEnabled : boolean = true;
+        public teleportationEnabled: boolean = true;
 
         private _currentHit: Nullable<PickingInfo>;
         private _pointerDownOnMeshAsked = false;
@@ -1407,6 +1407,15 @@ module BABYLON {
                 // The object selected is the floor, we're in a teleportation scenario
                 if (this._teleportationInitialized && this._isTeleportationFloor(hit.pickedMesh) && hit.pickedPoint) {
                     // Moving the teleportation area to this targetted point
+
+                    //Raise onSelectedMeshUnselected observable if ray collided floor mesh/meshes and a non floor mesh was previously selected
+                    if (this._currentMeshSelected &&
+                        !this._isTeleportationFloor(this._currentMeshSelected)) {
+                        this.onSelectedMeshUnselected.notifyObservers(this._currentMeshSelected);
+                    }
+
+                    this._currentMeshSelected = null;
+
                     this._moveTeleportationSelectorTo(hit);
                     return;
                 }
@@ -1428,7 +1437,7 @@ module BABYLON {
                             this._isActionableMesh = false;
                         }
                         try {
-                            this.onNewMeshSelected.notifyObservers(this._currentMeshSelected);                            
+                            this.onNewMeshSelected.notifyObservers(this._currentMeshSelected);
                         }
                         catch (err) {
                             Tools.Warn("Error in your custom logic onNewMeshSelected: " + err);

+ 38 - 20
src/Engine/babylon.engine.ts

@@ -281,7 +281,8 @@
         /** Use this array to turn off some WebGL2 features on known buggy browsers version */
         public static ExceptionList = [
             { key: "Chrome/63.0", capture: "63\\.0\\.3239\\.(\\d+)", captureConstraint: 108, targets: ["uniformBuffer"] },
-            { key: "Firefox/58", capture: null, captureConstraint: null, targets: ["uniformBuffer"] }
+            { key: "Firefox/58", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
+            { key: "Macintosh", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] },
         ];
 
         public static Instances = new Array<Engine>();
@@ -538,7 +539,7 @@
         }
 
         public static get Version(): string {
-            return "3.2.0-alpha2";
+            return "3.2.0-alpha4";
         }
 
         // Updatable statics so stick with vars here
@@ -646,8 +647,14 @@
             return this._badDesktopOS;
         }
 
-        public static audioEngine: AudioEngine;
+        /**
+         * Gets or sets a value indicating if we want to disable texture binding optmization.
+         * This could be required on some buggy drivers which wants to have textures bound in a progressive order
+         * By default Babylon.js will try to let textures bound where they are and only update the samplers to point where the texture is.
+         */
+        public disableTextureBindingOptimization = false;
 
+        public static audioEngine: AudioEngine;       
 
         // Focus
         private _onFocus: () => void;
@@ -764,6 +771,7 @@
         private _frameHandler: number;
 
         private _nextFreeTextureSlots = new Array<number>();
+        private _maxSimultaneousTextures = 0;
 
         private _activeRequests = new Array<IFileRequest>();
 
@@ -885,6 +893,9 @@
                                     case "uniformBuffer":
                                         this.disableUniformBuffers = true;
                                         break;
+                                    case "textureBindingOptimization":
+                                        this.disableTextureBindingOptimization = true;    
+                                        break;    
                                 }
                             }
                             break;
@@ -1295,7 +1306,8 @@
             this.setDepthWrite(true);
 
             // Texture maps
-            for (let slot = 0; slot < this._caps.maxCombinedTexturesImageUnits; slot++) {
+            this._maxSimultaneousTextures = this._caps.maxCombinedTexturesImageUnits;
+            for (let slot = 0; slot < this._maxSimultaneousTextures; slot++) {
                 this._nextFreeTextureSlots.push(slot);
             }
         }
@@ -1333,7 +1345,7 @@
                 this._boundTexturesCache[key] = null;
             }
             this._nextFreeTextureSlots = [];
-            for (let slot = 0; slot < this._caps.maxCombinedTexturesImageUnits; slot++) {
+            for (let slot = 0; slot < this._maxSimultaneousTextures; slot++) {
                 this._nextFreeTextureSlots.push(slot);
             }
             this._activeChannel = -1;
@@ -4771,7 +4783,7 @@
             let isTextureForRendering = texture && texture._initialSlot > -1;
 
             if (currentTextureBound !== texture) {
-                if (currentTextureBound) {
+                if (currentTextureBound && !this.disableTextureBindingOptimization) {
                     this._removeDesignatedSlot(currentTextureBound);
                 }
 
@@ -4780,7 +4792,7 @@
                 if (this._activeChannel >= 0) {
                     this._boundTexturesCache[this._activeChannel] = texture;
 
-                    if (isTextureForRendering) {
+                    if (isTextureForRendering && !this.disableTextureBindingOptimization) {
                         let slotIndex = this._nextFreeTextureSlots.indexOf(this._activeChannel);
                         if (slotIndex > -1) {
                             this._nextFreeTextureSlots.splice(slotIndex, 1);
@@ -4816,7 +4828,7 @@
         }
 
         public unbindAllTextures(): void {
-            for (var channel = 0; channel < this._caps.maxCombinedTexturesImageUnits; channel++) {
+            for (var channel = 0; channel < this._maxSimultaneousTextures; channel++) {
                 this._activateTextureChannel(channel);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, null);
@@ -4845,20 +4857,26 @@
 
             internalTexture._initialSlot = channel;
 
-            if (channel !== internalTexture._designatedSlot) {
-                if (internalTexture._designatedSlot > -1) { // Texture is already assigned to a slot
-                    return internalTexture._designatedSlot;
-                } else {
-                    // No slot for this texture, let's pick a new one (if we find a free slot)
-                    if (this._nextFreeTextureSlots.length) {
-                        return this._nextFreeTextureSlots[0];
-                    }
-
-                    // We need to recycle the oldest bound texture, sorry.
+            if (this.disableTextureBindingOptimization) { // We want texture sampler ID == texture channel
+                if (channel !== internalTexture._designatedSlot) {              
                     this._textureCollisions.addCount(1, false);
-                    return this._removeDesignatedSlot(this._boundTexturesStack[0]);
                 }
-            }
+            } else {          
+                if (channel !== internalTexture._designatedSlot) {
+                    if (internalTexture._designatedSlot > -1) { // Texture is already assigned to a slot
+                        return internalTexture._designatedSlot;
+                    } else {
+                        // No slot for this texture, let's pick a new one (if we find a free slot)
+                        if (this._nextFreeTextureSlots.length) {
+                            return this._nextFreeTextureSlots[0];
+                        }
+
+                        // We need to recycle the oldest bound texture, sorry.
+                        this._textureCollisions.addCount(1, false);
+                        return this._removeDesignatedSlot(this._boundTexturesStack[0]);
+                    }
+                }
+            }    
 
             return channel;
         }

+ 41 - 0
src/Gamepad/Controllers/babylon.gearVRController.ts

@@ -0,0 +1,41 @@
+module BABYLON {
+    
+    export class GearVRController extends WebVRController {
+        public static MODEL_BASE_URL:string = 'https://controllers.babylonjs.com/generic/';
+        public static MODEL_FILENAME:string = 'generic.babylon';
+
+        public static readonly GAMEPAD_ID_PREFIX: string = 'Gear VR'; // id is 'Gear VR Controller'
+
+        private readonly _buttonIndexToObservableNameMap = [
+            'onTrackpadChangedObservable', // Trackpad
+            'onTriggerStateChangedObservable' // Trigger
+        ]
+
+        constructor(vrGamepad: any) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.GEAR_VR;
+        }
+
+        public initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void) {
+            SceneLoader.ImportMesh("", GearVRController.MODEL_BASE_URL, GearVRController.MODEL_FILENAME, scene, (newMeshes) => {
+                this._defaultModel = newMeshes[1];
+                this.attachToMesh(this._defaultModel);
+                if (meshLoaded) {
+                    meshLoaded(this._defaultModel);
+                }
+            });
+        }
+
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            if (buttonIdx < this._buttonIndexToObservableNameMap.length) {
+                const observableName : string = this._buttonIndexToObservableNameMap[buttonIdx];
+                
+                // Only emit events for buttons that we know how to map from index to observable
+                let observable = (<any>this)[observableName];
+                if (observable) {
+                    observable.notifyObservers(state);
+                }
+            }
+        }
+    }
+}

+ 5 - 0
src/Gamepad/Controllers/babylon.poseEnabledController.ts

@@ -4,6 +4,7 @@ module BABYLON {
         VIVE,
         OCULUS,
         WINDOWS,
+        GEAR_VR,
         GENERIC
     }
 
@@ -33,6 +34,10 @@ module BABYLON {
             else if (vrGamepad.id.toLowerCase().indexOf('openvr') !== -1) {
                 return new ViveController(vrGamepad);
             }
+            // Samsung/Oculus Gear VR
+            else if (vrGamepad.id.indexOf(GearVRController.GAMEPAD_ID_PREFIX) === 0) {
+                return new GearVRController(vrGamepad);
+            }
             // Generic 
             else {
                 return new GenericController(vrGamepad);

+ 39 - 2
src/Layer/babylon.highlightlayer.ts

@@ -456,7 +456,7 @@
                     emissiveTexture = (<any>material).emissiveTexture;
                 }
 
-                if (this.isReady(subMesh, hardwareInstancedRendering, emissiveTexture)) {
+                if (this._isReady(subMesh, hardwareInstancedRendering, emissiveTexture)) {
                     engine.enableEffect(this._glowMapGenerationEffect);
                     mesh._bind(subMesh, this._glowMapGenerationEffect, Material.TriangleFillMode);
 
@@ -549,7 +549,31 @@
          * @param emissiveTexture the associated emissive texture used to generate the glow
          * @return true if ready otherwise, false
          */
-        private isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<Texture>): boolean {
+        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+            let material = subMesh.getMaterial();
+            let mesh = subMesh.getRenderingMesh();
+
+            if (!material || !mesh || !this._meshes) {
+                return false;
+            }
+
+            let emissiveTexture: Nullable<Texture> = null;
+            let highlightLayerMesh = this._meshes[mesh.uniqueId];
+
+            if (highlightLayerMesh && highlightLayerMesh.glowEmissiveOnly && material) {
+                emissiveTexture = (<any>material).emissiveTexture;
+            }
+            return this._isReady(subMesh, useInstances, emissiveTexture);
+        }
+
+        /**
+         * Checks for the readiness of the element composing the layer.
+         * @param subMesh the mesh to check for
+         * @param useInstances specify wether or not to use instances to render the mesh
+         * @param emissiveTexture the associated emissive texture used to generate the glow
+         * @return true if ready otherwise, false
+         */
+        private _isReady(subMesh: SubMesh, useInstances: boolean, emissiveTexture: Nullable<Texture>): boolean {
             let material = subMesh.getMaterial();
 
             if (!material) {
@@ -770,6 +794,19 @@
         }
 
         /**
+         * Determine if a given mesh will be highlighted by the current HighlightLayer
+         * @param mesh mesh to test
+         * @returns true if the mesh will be highlighted by the current HighlightLayer
+         */
+        public hasMesh(mesh: AbstractMesh): boolean {
+            if (!this._meshes) {
+                return false;
+            }
+
+            return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
+        }
+
+        /**
          * Add a mesh in the highlight layer in order to make it glow with the chosen color.
          * @param mesh The mesh to highlight
          * @param color The color of the highlight

+ 21 - 11
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -345,7 +345,6 @@
         private _cachedDirection: Vector3;
         private _cachedDefines: string;
         private _currentRenderID: number;
-        private _downSamplePostprocess: Nullable<PassPostProcess>;
         private _boxBlurPostprocess: Nullable<PostProcess>;
         private _kernelBlurXPostprocess: Nullable<PostProcess>;
         private _kernelBlurYPostprocess: Nullable<PostProcess>;
@@ -428,10 +427,6 @@
                 if (!this.useBlurExponentialShadowMap && !this.useBlurCloseExponentialShadowMap) {
                     return;
                 }
-
-                if (!this._blurPostProcesses || !this._blurPostProcesses.length) {
-                    this._initializeBlurRTTAndPostProcesses();
-                }
                 let shadowMap = this.getShadowMapForRendering();
 
                 if (shadowMap) {
@@ -727,7 +722,27 @@
                     ["diffuseSampler"], join);
             }
 
-            return this._effect.isReady();
+            if (!this._effect.isReady()) {
+                return false;
+            }
+
+            if (this.useBlurExponentialShadowMap || this.useBlurCloseExponentialShadowMap) {
+                if (!this._blurPostProcesses || !this._blurPostProcesses.length) {
+                    this._initializeBlurRTTAndPostProcesses();
+                }
+            }
+
+            if (this._kernelBlurXPostprocess && !this._kernelBlurXPostprocess.isReady()) {
+                return false;
+            }
+            if (this._kernelBlurYPostprocess && !this._kernelBlurYPostprocess.isReady()) {
+                return false;
+            }
+            if (this._boxBlurPostprocess && !this._boxBlurPostprocess.isReady()) {
+                return false;
+            }
+
+            return true;
         }
 
         /**
@@ -861,11 +876,6 @@
                 this._shadowMap2 = null;
             }
 
-            if (this._downSamplePostprocess) {
-                this._downSamplePostprocess.dispose();
-                this._downSamplePostprocess = null;
-            }
-
             if (this._boxBlurPostprocess) {
                 this._boxBlurPostprocess.dispose();
                 this._boxBlurPostprocess = null;

+ 1 - 1
src/Lights/babylon.light.ts

@@ -18,7 +18,7 @@
 
         /**
          * material.lightmapTexture as only diffuse lighting from this light
-         * adds pnly specular lighting from this light
+         * adds only specular lighting from this light
          * adds dynamic shadows
          */
         public static get LIGHTMAP_SPECULAR(): number {

+ 480 - 418
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -28,6 +28,357 @@
         return operation + " of " + (producer ? producer.file + " from " + producer.name + " version: " + producer.version + ", exporter version: " + producer.exporter_version : "unknown");
     }
 
+    var loadAssets = (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void, addToScene = false):Nullable<AssetContainer> => {
+        var container = new AssetContainer(scene);
+
+            // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
+            // when SceneLoader.debugLogging = true (default), or exception encountered.
+            // Everything stored in var log instead of writing separate lines to support only writing in exception,
+            // and avoid problems with multiple concurrent .babylon loads.
+            var log = "importScene has failed JSON parse";
+            try {
+                var parsedData = JSON.parse(data);
+                log = "";
+                var fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;               
+
+                var index: number;
+                var cache: number;
+                // Lights
+                if (parsedData.lights !== undefined && parsedData.lights !== null) {
+                    for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
+                        var parsedLight = parsedData.lights[index];
+                        var light = Light.Parse(parsedLight, scene);
+                        if (light) {
+                            container.lights.push(light);
+                            log += (index === 0 ? "\n\tLights:" : "");
+                            log += "\n\t\t" + light.toString(fullDetails);
+                        }
+                    }
+                }
+
+                // Animations
+                if (parsedData.animations !== undefined && parsedData.animations !== null) {
+                    for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
+                        var parsedAnimation = parsedData.animations[index];
+                        var animation = Animation.Parse(parsedAnimation);
+                        scene.animations.push(animation);
+                        container.animations.push(animation);
+                        log += (index === 0 ? "\n\tAnimations:" : "");
+                        log += "\n\t\t" + animation.toString(fullDetails);
+                    }
+                }
+
+                // Materials
+                if (parsedData.materials !== undefined && parsedData.materials !== null) {
+                    for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
+                        var parsedMaterial = parsedData.materials[index];
+                        var mat = Material.Parse(parsedMaterial, scene, rootUrl);
+                        container.materials.push(mat);
+                        log += (index === 0 ? "\n\tMaterials:" : "");
+                        log += "\n\t\t" + mat.toString(fullDetails);
+                    }
+                }
+
+                if (parsedData.multiMaterials !== undefined && parsedData.multiMaterials !== null) {
+                    for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
+                        var parsedMultiMaterial = parsedData.multiMaterials[index];
+                        var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
+                        container.multiMaterials.push(mmat);
+                        log += (index === 0 ? "\n\tMultiMaterials:" : "");
+                        log += "\n\t\t" + mmat.toString(fullDetails);
+                    }
+                }
+
+                // Morph targets
+                if (parsedData.morphTargetManagers !== undefined && parsedData.morphTargetManagers !== null) {
+                    for (var managerData of parsedData.morphTargetManagers) {
+                        container.morphTargetManagers.push(MorphTargetManager.Parse(managerData, scene));
+                    }
+                }
+
+                // Skeletons
+                if (parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
+                    for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
+                        var parsedSkeleton = parsedData.skeletons[index];
+                        var skeleton = Skeleton.Parse(parsedSkeleton, scene);
+                        container.skeletons.push(skeleton);
+                        log += (index === 0 ? "\n\tSkeletons:" : "");
+                        log += "\n\t\t" + skeleton.toString(fullDetails);
+                    }
+                }
+
+                // Geometries
+                var geometries = parsedData.geometries;
+                if (geometries !== undefined && geometries !== null) {
+                    var addedGeometry = new Array<Nullable<Geometry>>();
+                    // Boxes
+                    var boxes = geometries.boxes;
+                    if (boxes !== undefined && boxes !== null) {
+                        for (index = 0, cache = boxes.length; index < cache; index++) {
+                            var parsedBox = boxes[index];
+                            addedGeometry.push(BoxGeometry.Parse(parsedBox, scene));
+                        }
+                    }
+
+                    // Spheres
+                    var spheres = geometries.spheres;
+                    if (spheres !== undefined && spheres !== null) {
+                        for (index = 0, cache = spheres.length; index < cache; index++) {
+                            var parsedSphere = spheres[index];
+                            addedGeometry.push(SphereGeometry.Parse(parsedSphere, scene));
+                        }
+                    }
+
+                    // Cylinders
+                    var cylinders = geometries.cylinders;
+                    if (cylinders !== undefined && cylinders !== null) {
+                        for (index = 0, cache = cylinders.length; index < cache; index++) {
+                            var parsedCylinder = cylinders[index];
+                            addedGeometry.push(CylinderGeometry.Parse(parsedCylinder, scene));
+                        }
+                    }
+
+                    // Toruses
+                    var toruses = geometries.toruses;
+                    if (toruses !== undefined && toruses !== null) {
+                        for (index = 0, cache = toruses.length; index < cache; index++) {
+                            var parsedTorus = toruses[index];
+                            addedGeometry.push(TorusGeometry.Parse(parsedTorus, scene));
+                        }
+                    }
+
+                    // Grounds
+                    var grounds = geometries.grounds;
+                    if (grounds !== undefined && grounds !== null) {
+                        for (index = 0, cache = grounds.length; index < cache; index++) {
+                            var parsedGround = grounds[index];
+                            addedGeometry.push(GroundGeometry.Parse(parsedGround, scene));
+                        }
+                    }
+
+                    // Planes
+                    var planes = geometries.planes;
+                    if (planes !== undefined && planes !== null) {
+                        for (index = 0, cache = planes.length; index < cache; index++) {
+                            var parsedPlane = planes[index];
+                            addedGeometry.push(PlaneGeometry.Parse(parsedPlane, scene));
+                        }
+                    }
+
+                    // TorusKnots
+                    var torusKnots = geometries.torusKnots;
+                    if (torusKnots !== undefined && torusKnots !== null) {
+                        for (index = 0, cache = torusKnots.length; index < cache; index++) {
+                            var parsedTorusKnot = torusKnots[index];
+                            addedGeometry.push(TorusKnotGeometry.Parse(parsedTorusKnot, scene));
+                        }
+                    }
+
+                    // VertexData
+                    var vertexData = geometries.vertexData;
+                    if (vertexData !== undefined && vertexData !== null) {
+                        for (index = 0, cache = vertexData.length; index < cache; index++) {
+                            var parsedVertexData = vertexData[index];
+                            addedGeometry.push(Geometry.Parse(parsedVertexData, scene, rootUrl));
+                        }
+                    }
+
+                    addedGeometry.forEach((g)=>{
+                        if(g){
+                            container.geometries.push(g);
+                        }
+                    })
+                }
+                
+                // Transform nodes
+                if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
+                    for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
+                        var parsedTransformNode = parsedData.transformNodes[index];
+                        var node = TransformNode.Parse(parsedTransformNode, scene, rootUrl);
+                        container.transformNodes.push(node);
+                    }
+                }                
+
+                // Meshes
+                if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
+                    for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
+                        var parsedMesh = parsedData.meshes[index];
+                        var mesh = <AbstractMesh>Mesh.Parse(parsedMesh, scene, rootUrl);
+                        container.meshes.push(mesh);
+                        log += (index === 0 ? "\n\tMeshes:" : "");
+                        log += "\n\t\t" + mesh.toString(fullDetails);
+                    }
+                }
+
+                // Cameras
+                if (parsedData.cameras !== undefined && parsedData.cameras !== null) {
+                    for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
+                        var parsedCamera = parsedData.cameras[index];
+                        var camera = Camera.Parse(parsedCamera, scene);
+                        container.cameras.push(camera);
+                        log += (index === 0 ? "\n\tCameras:" : "");
+                        log += "\n\t\t" + camera.toString(fullDetails);
+                    }
+                }
+
+                // Browsing all the graph to connect the dots
+                for (index = 0, cache = scene.cameras.length; index < cache; index++) {
+                    var camera = scene.cameras[index];
+                    if (camera._waitingParentId) {
+                        camera.parent = scene.getLastEntryByID(camera._waitingParentId);
+                        camera._waitingParentId = null;
+                    }
+                }
+
+                for (index = 0, cache = scene.lights.length; index < cache; index++) {
+                    let light = scene.lights[index];
+                    if (light && light._waitingParentId) {
+                        light.parent = scene.getLastEntryByID(light._waitingParentId);
+                        light._waitingParentId = null;
+                    }
+                }
+
+                // Sounds
+                // TODO: add sound
+                var loadedSounds: Sound[] = [];
+                var loadedSound: Sound;
+                if (AudioEngine && parsedData.sounds !== undefined && parsedData.sounds !== null) {
+                    for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
+                        var parsedSound = parsedData.sounds[index];
+                        if (Engine.audioEngine.canUseWebAudio) {
+                            if (!parsedSound.url) parsedSound.url = parsedSound.name;
+                            if (!loadedSounds[parsedSound.url]) {
+                                loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
+                                loadedSounds[parsedSound.url] = loadedSound;
+                                container.sounds.push(loadedSound);
+                            }
+                            else {
+                                container.sounds.push(Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]));
+                            }
+                        } else {
+                            container.sounds.push(new Sound(parsedSound.name, null, scene));
+                        }
+                    }
+                }
+
+                loadedSounds = [];
+
+                // Connect parents & children and parse actions
+                for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
+                    var transformNode = scene.transformNodes[index];
+                    if (transformNode._waitingParentId) {
+                        transformNode.parent = scene.getLastEntryByID(transformNode._waitingParentId);
+                        transformNode._waitingParentId = null;
+                    }
+                }                
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                    var mesh = scene.meshes[index];
+                    if (mesh._waitingParentId) {
+                        mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
+                        mesh._waitingParentId = null;
+                    }
+                    if (mesh._waitingActions) {
+                        ActionManager.Parse(mesh._waitingActions, mesh, scene);
+                        mesh._waitingActions = null;
+                    }
+                }
+
+                // freeze world matrix application
+                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                    var currentMesh = scene.meshes[index];
+                    if (currentMesh._waitingFreezeWorldMatrix) {
+                        currentMesh.freezeWorldMatrix();
+                        currentMesh._waitingFreezeWorldMatrix = null;
+                    } else {
+                        currentMesh.computeWorldMatrix(true);
+                    }
+                }
+
+                // Particles Systems
+                if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
+                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
+                        var parsedParticleSystem = parsedData.particleSystems[index];
+                        var ps = ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
+                        container.particleSystems.push(ps);
+                    }
+                }
+
+                // Lens flares
+                if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
+                    for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
+                        var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
+                        var lf = LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
+                        container.lensFlareSystems.push(lf);
+                    }
+                }
+
+                // Shadows
+                if (parsedData.shadowGenerators !== undefined && parsedData.shadowGenerators !== null) {
+                    for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
+                        var parsedShadowGenerator = parsedData.shadowGenerators[index];
+                        var sg = ShadowGenerator.Parse(parsedShadowGenerator, scene);
+                        container.shadowGenerators.push(sg);
+                    }
+                }
+
+                // Lights exclusions / inclusions
+                for (index = 0, cache = scene.lights.length; index < cache; index++) {
+                    let light = scene.lights[index];
+                    // Excluded check
+                    if (light._excludedMeshesIds.length > 0) {
+                        for (var excludedIndex = 0; excludedIndex < light._excludedMeshesIds.length; excludedIndex++) {
+                            var excludedMesh = scene.getMeshByID(light._excludedMeshesIds[excludedIndex]);
+
+                            if (excludedMesh) {
+                                light.excludedMeshes.push(excludedMesh);
+                            }
+                        }
+
+                        light._excludedMeshesIds = [];
+                    }
+
+                    // Included check
+                    if (light._includedOnlyMeshesIds.length > 0) {
+                        for (var includedOnlyIndex = 0; includedOnlyIndex < light._includedOnlyMeshesIds.length; includedOnlyIndex++) {
+                            var includedOnlyMesh = scene.getMeshByID(light._includedOnlyMeshesIds[includedOnlyIndex]);
+
+                            if (includedOnlyMesh) {
+                                light.includedOnlyMeshes.push(includedOnlyMesh);
+                            }
+                        }
+
+                        light._includedOnlyMeshesIds = [];
+                    }
+                }
+
+                // Actions (scene)
+                if (parsedData.actions !== undefined && parsedData.actions !== null) {
+                    ActionManager.Parse(parsedData.actions, null, scene);
+                }
+                
+                if(!addToScene){
+                    container.removeAllFromScene();
+                }
+
+                return container;
+
+            } catch (err) {
+                let msg = logOperation("loadAssts", parsedData ? parsedData.producer : "Unknown") + log;
+                if (onError) {
+                    onError(msg, err);
+                } else {
+                    Tools.Log(msg);
+                    throw err;
+                }
+            } finally {
+                if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
+                    Tools.Log(logOperation("loadAssts", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
+                }
+            }
+
+            return null;
+    }
+
     SceneLoader.RegisterPlugin({
         name: "babylon.js",
         extensions: ".babylon",
@@ -150,464 +501,172 @@
                                         log += "\n\tMaterial " + mat.toString(fullDetails);
                                     }
                                 }
-                            }
-
-                            // Skeleton ?
-                            if (parsedMesh.skeletonId > -1 && parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
-                                var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
-                                if (skeletonAlreadyLoaded === false) {
-                                    for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
-                                        var parsedSkeleton = parsedData.skeletons[skeletonIndex];
-                                        if (parsedSkeleton.id === parsedMesh.skeletonId) {
-                                            var skeleton = Skeleton.Parse(parsedSkeleton, scene);
-                                            skeletons.push(skeleton);
-                                            loadedSkeletonsIds.push(parsedSkeleton.id);
-                                            log += "\n\tSkeleton " + skeleton.toString(fullDetails);
-                                        }
-                                    }
-                                }
-                            }
-
-                            var mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
-                            meshes.push(mesh);
-                            log += "\n\tMesh " + mesh.toString(fullDetails);
-                        }
-                    }
-
-                    // Connecting parents
-                    var currentMesh: AbstractMesh;
-                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                        currentMesh = scene.meshes[index];
-                        if (currentMesh._waitingParentId) {
-                            currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
-                            currentMesh._waitingParentId = null;
-                        }
-                    }
-
-                    // freeze and compute world matrix application
-                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                        currentMesh = scene.meshes[index];
-                        if (currentMesh._waitingFreezeWorldMatrix) {
-                            currentMesh.freezeWorldMatrix();
-                            currentMesh._waitingFreezeWorldMatrix = null;
-                        } else {
-                            currentMesh.computeWorldMatrix(true);
-                        }
-                    }
-                }
-
-                // Particles
-                if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
-                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
-                        var parsedParticleSystem = parsedData.particleSystems[index];
-                        if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
-                            particleSystems.push(ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl));
-                        }
-                    }
-                }
-
-                return true;
-
-            } catch (err) {
-                let msg = logOperation("importMesh", parsedData ? parsedData.producer : "Unknown") + log;
-                if (onError) {
-                    onError(msg, err);
-                } else {
-                    Tools.Log(msg);
-                    throw err;
-                }
-            } finally {
-                if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
-                    Tools.Log(logOperation("importMesh", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
-                }
-            }
-
-            return false;
-        },
-        load: (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): boolean => {
-            // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
-            // when SceneLoader.debugLogging = true (default), or exception encountered.
-            // Everything stored in var log instead of writing separate lines to support only writing in exception,
-            // and avoid problems with multiple concurrent .babylon loads.
-            var log = "importScene has failed JSON parse";
-            try {
-                var parsedData = JSON.parse(data);
-                log = "";
-                var fullDetails = SceneLoader.loggingLevel === SceneLoader.DETAILED_LOGGING;
-
-                // Scene
-                if (parsedData.useDelayedTextureLoading !== undefined && parsedData.useDelayedTextureLoading !== null) {
-                    scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !SceneLoader.ForceFullSceneLoadingForIncremental;
-                }
-                if (parsedData.autoClear !== undefined && parsedData.autoClear !== null) {
-                    scene.autoClear = parsedData.autoClear;
-                }
-                if (parsedData.clearColor !== undefined && parsedData.clearColor !== null) {
-                    scene.clearColor = Color4.FromArray(parsedData.clearColor);
-                }
-                if (parsedData.ambientColor !== undefined && parsedData.ambientColor !== null) {
-                    scene.ambientColor = Color3.FromArray(parsedData.ambientColor);
-                }
-                if (parsedData.gravity !== undefined && parsedData.gravity !== null) {
-                    scene.gravity = Vector3.FromArray(parsedData.gravity);
-                }
-
-                // Fog
-                if (parsedData.fogMode && parsedData.fogMode !== 0) {
-                    scene.fogMode = parsedData.fogMode;
-                    scene.fogColor = Color3.FromArray(parsedData.fogColor);
-                    scene.fogStart = parsedData.fogStart;
-                    scene.fogEnd = parsedData.fogEnd;
-                    scene.fogDensity = parsedData.fogDensity;
-                    log += "\tFog mode for scene:  ";
-                    switch (scene.fogMode) {
-                        // getters not compiling, so using hardcoded
-                        case 1: log += "exp\n"; break;
-                        case 2: log += "exp2\n"; break;
-                        case 3: log += "linear\n"; break;
-                    }
-                }
-
-                //Physics
-                if (parsedData.physicsEnabled) {
-                    var physicsPlugin;
-                    if (parsedData.physicsEngine === "cannon") {
-                        physicsPlugin = new CannonJSPlugin();
-                    } else if (parsedData.physicsEngine === "oimo") {
-                        physicsPlugin = new OimoJSPlugin();
-                    }
-                    log = "\tPhysics engine " + (parsedData.physicsEngine ? parsedData.physicsEngine : "oimo") + " enabled\n";
-                    //else - default engine, which is currently oimo
-                    var physicsGravity = parsedData.physicsGravity ? Vector3.FromArray(parsedData.physicsGravity) : null;
-                    scene.enablePhysics(physicsGravity, physicsPlugin);
-                }
-
-                // Metadata
-                if (parsedData.metadata !== undefined && parsedData.metadata !== null) {
-                    scene.metadata = parsedData.metadata;
-                }
-
-                //collisions, if defined. otherwise, default is true
-                if (parsedData.collisionsEnabled !== undefined && parsedData.collisionsEnabled !== null) {
-                    scene.collisionsEnabled = parsedData.collisionsEnabled;
-                }
-                scene.workerCollisions = !!parsedData.workerCollisions;
-
-                var index: number;
-                var cache: number;
-                // Lights
-                if (parsedData.lights !== undefined && parsedData.lights !== null) {
-                    for (index = 0, cache = parsedData.lights.length; index < cache; index++) {
-                        var parsedLight = parsedData.lights[index];
-                        var light = Light.Parse(parsedLight, scene);
-                        if (light) {
-                            log += (index === 0 ? "\n\tLights:" : "");
-                            log += "\n\t\t" + light.toString(fullDetails);
-                        }
-                    }
-                }
-
-                // Animations
-                if (parsedData.animations !== undefined && parsedData.animations !== null) {
-                    for (index = 0, cache = parsedData.animations.length; index < cache; index++) {
-                        var parsedAnimation = parsedData.animations[index];
-                        var animation = Animation.Parse(parsedAnimation);
-                        scene.animations.push(animation);
-                        log += (index === 0 ? "\n\tAnimations:" : "");
-                        log += "\n\t\t" + animation.toString(fullDetails);
-                    }
-                }
-
-                if (parsedData.autoAnimate) {
-                    scene.beginAnimation(scene, parsedData.autoAnimateFrom, parsedData.autoAnimateTo, parsedData.autoAnimateLoop, parsedData.autoAnimateSpeed || 1.0);
-                }
-
-                // Materials
-                if (parsedData.materials !== undefined && parsedData.materials !== null) {
-                    for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
-                        var parsedMaterial = parsedData.materials[index];
-                        var mat = Material.Parse(parsedMaterial, scene, rootUrl);
-                        log += (index === 0 ? "\n\tMaterials:" : "");
-                        log += "\n\t\t" + mat.toString(fullDetails);
-                    }
-                }
-
-                if (parsedData.multiMaterials !== undefined && parsedData.multiMaterials !== null) {
-                    for (index = 0, cache = parsedData.multiMaterials.length; index < cache; index++) {
-                        var parsedMultiMaterial = parsedData.multiMaterials[index];
-                        var mmat = Material.ParseMultiMaterial(parsedMultiMaterial, scene);
-                        log += (index === 0 ? "\n\tMultiMaterials:" : "");
-                        log += "\n\t\t" + mmat.toString(fullDetails);
-                    }
-                }
-
-                // Morph targets
-                if (parsedData.morphTargetManagers !== undefined && parsedData.morphTargetManagers !== null) {
-                    for (var managerData of parsedData.morphTargetManagers) {
-                        MorphTargetManager.Parse(managerData, scene);
-                    }
-                }
-
-                // Skeletons
-                if (parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
-                    for (index = 0, cache = parsedData.skeletons.length; index < cache; index++) {
-                        var parsedSkeleton = parsedData.skeletons[index];
-                        var skeleton = Skeleton.Parse(parsedSkeleton, scene);
-                        log += (index === 0 ? "\n\tSkeletons:" : "");
-                        log += "\n\t\t" + skeleton.toString(fullDetails);
-                    }
-                }
-
-                // Geometries
-                var geometries = parsedData.geometries;
-                if (geometries !== undefined && geometries !== null) {
-                    // Boxes
-                    var boxes = geometries.boxes;
-                    if (boxes !== undefined && boxes !== null) {
-                        for (index = 0, cache = boxes.length; index < cache; index++) {
-                            var parsedBox = boxes[index];
-                            BoxGeometry.Parse(parsedBox, scene);
-                        }
-                    }
-
-                    // Spheres
-                    var spheres = geometries.spheres;
-                    if (spheres !== undefined && spheres !== null) {
-                        for (index = 0, cache = spheres.length; index < cache; index++) {
-                            var parsedSphere = spheres[index];
-                            SphereGeometry.Parse(parsedSphere, scene);
-                        }
-                    }
-
-                    // Cylinders
-                    var cylinders = geometries.cylinders;
-                    if (cylinders !== undefined && cylinders !== null) {
-                        for (index = 0, cache = cylinders.length; index < cache; index++) {
-                            var parsedCylinder = cylinders[index];
-                            CylinderGeometry.Parse(parsedCylinder, scene);
-                        }
-                    }
-
-                    // Toruses
-                    var toruses = geometries.toruses;
-                    if (toruses !== undefined && toruses !== null) {
-                        for (index = 0, cache = toruses.length; index < cache; index++) {
-                            var parsedTorus = toruses[index];
-                            TorusGeometry.Parse(parsedTorus, scene);
-                        }
-                    }
-
-                    // Grounds
-                    var grounds = geometries.grounds;
-                    if (grounds !== undefined && grounds !== null) {
-                        for (index = 0, cache = grounds.length; index < cache; index++) {
-                            var parsedGround = grounds[index];
-                            GroundGeometry.Parse(parsedGround, scene);
-                        }
-                    }
+                            }
 
-                    // Planes
-                    var planes = geometries.planes;
-                    if (planes !== undefined && planes !== null) {
-                        for (index = 0, cache = planes.length; index < cache; index++) {
-                            var parsedPlane = planes[index];
-                            PlaneGeometry.Parse(parsedPlane, scene);
+                            // Skeleton ?
+                            if (parsedMesh.skeletonId > -1 && parsedData.skeletons !== undefined && parsedData.skeletons !== null) {
+                                var skeletonAlreadyLoaded = (loadedSkeletonsIds.indexOf(parsedMesh.skeletonId) > -1);
+                                if (skeletonAlreadyLoaded === false) {
+                                    for (var skeletonIndex = 0, skeletonCache = parsedData.skeletons.length; skeletonIndex < skeletonCache; skeletonIndex++) {
+                                        var parsedSkeleton = parsedData.skeletons[skeletonIndex];
+                                        if (parsedSkeleton.id === parsedMesh.skeletonId) {
+                                            var skeleton = Skeleton.Parse(parsedSkeleton, scene);
+                                            skeletons.push(skeleton);
+                                            loadedSkeletonsIds.push(parsedSkeleton.id);
+                                            log += "\n\tSkeleton " + skeleton.toString(fullDetails);
+                                        }
+                                    }
+                                }
+                            }
+
+                            var mesh = Mesh.Parse(parsedMesh, scene, rootUrl);
+                            meshes.push(mesh);
+                            log += "\n\tMesh " + mesh.toString(fullDetails);
                         }
                     }
 
-                    // TorusKnots
-                    var torusKnots = geometries.torusKnots;
-                    if (torusKnots !== undefined && torusKnots !== null) {
-                        for (index = 0, cache = torusKnots.length; index < cache; index++) {
-                            var parsedTorusKnot = torusKnots[index];
-                            TorusKnotGeometry.Parse(parsedTorusKnot, scene);
+                    // Connecting parents
+                    var currentMesh: AbstractMesh;
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingParentId) {
+                            currentMesh.parent = scene.getLastEntryByID(currentMesh._waitingParentId);
+                            currentMesh._waitingParentId = null;
                         }
                     }
 
-                    // VertexData
-                    var vertexData = geometries.vertexData;
-                    if (vertexData !== undefined && vertexData !== null) {
-                        for (index = 0, cache = vertexData.length; index < cache; index++) {
-                            var parsedVertexData = vertexData[index];
-                            Geometry.Parse(parsedVertexData, scene, rootUrl);
+                    // freeze and compute world matrix application
+                    for (index = 0, cache = scene.meshes.length; index < cache; index++) {
+                        currentMesh = scene.meshes[index];
+                        if (currentMesh._waitingFreezeWorldMatrix) {
+                            currentMesh.freezeWorldMatrix();
+                            currentMesh._waitingFreezeWorldMatrix = null;
+                        } else {
+                            currentMesh.computeWorldMatrix(true);
                         }
                     }
                 }
 
-                // Transform nodes
-                if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
-                    for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
-                        var parsedTransformNode = parsedData.transformNodes[index];
-                        TransformNode.Parse(parsedTransformNode, scene, rootUrl);
+                // Particles
+                if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
+                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
+                        var parsedParticleSystem = parsedData.particleSystems[index];
+                        if (hierarchyIds.indexOf(parsedParticleSystem.emitterId) !== -1) {
+                            particleSystems.push(ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl));
+                        }
                     }
                 }
 
-                // Meshes
-                if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
-                    for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
-                        var parsedMesh = parsedData.meshes[index];
-                        var mesh = <AbstractMesh>Mesh.Parse(parsedMesh, scene, rootUrl);
-                        log += (index === 0 ? "\n\tMeshes:" : "");
-                        log += "\n\t\t" + mesh.toString(fullDetails);
-                    }
-                }
+                return true;
 
-                // Cameras
-                if (parsedData.cameras !== undefined && parsedData.cameras !== null) {
-                    for (index = 0, cache = parsedData.cameras.length; index < cache; index++) {
-                        var parsedCamera = parsedData.cameras[index];
-                        var camera = Camera.Parse(parsedCamera, scene);
-                        log += (index === 0 ? "\n\tCameras:" : "");
-                        log += "\n\t\t" + camera.toString(fullDetails);
-                    }
+            } catch (err) {
+                let msg = logOperation("importMesh", parsedData ? parsedData.producer : "Unknown") + log;
+                if (onError) {
+                    onError(msg, err);
+                } else {
+                    Tools.Log(msg);
+                    throw err;
                 }
-                if (parsedData.activeCameraID !== undefined && parsedData.activeCameraID !== null) {
-                    scene.setActiveCameraByID(parsedData.activeCameraID);
+            } finally {
+                if (log !== null && SceneLoader.loggingLevel !== SceneLoader.NO_LOGGING) {
+                    Tools.Log(logOperation("importMesh", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
                 }
+            }
 
-                // Browsing all the graph to connect the dots
-                for (index = 0, cache = scene.cameras.length; index < cache; index++) {
-                    var camera = scene.cameras[index];
-                    if (camera._waitingParentId) {
-                        camera.parent = scene.getLastEntryByID(camera._waitingParentId);
-                        camera._waitingParentId = null;
-                    }
-                }
+            return false;
+        },
+        load: (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): boolean => {
+            // Entire method running in try block, so ALWAYS logs as far as it got, only actually writes details
+            // when SceneLoader.debugLogging = true (default), or exception encountered.
+            // Everything stored in var log instead of writing separate lines to support only writing in exception,
+            // and avoid problems with multiple concurrent .babylon loads.
+            var log = "importScene has failed JSON parse";
+            try {
+                var parsedData = JSON.parse(data);
+                log = "";
 
-                for (index = 0, cache = scene.lights.length; index < cache; index++) {
-                    let light = scene.lights[index];
-                    if (light && light._waitingParentId) {
-                        light.parent = scene.getLastEntryByID(light._waitingParentId);
-                        light._waitingParentId = null;
-                    }
+                // Scene
+                if (parsedData.useDelayedTextureLoading !== undefined && parsedData.useDelayedTextureLoading !== null) {
+                    scene.useDelayedTextureLoading = parsedData.useDelayedTextureLoading && !SceneLoader.ForceFullSceneLoadingForIncremental;
                 }
-
-                // Sounds
-                var loadedSounds: Sound[] = [];
-                var loadedSound: Sound;
-                if (AudioEngine && parsedData.sounds !== undefined && parsedData.sounds !== null) {
-                    for (index = 0, cache = parsedData.sounds.length; index < cache; index++) {
-                        var parsedSound = parsedData.sounds[index];
-                        if (Engine.audioEngine.canUseWebAudio) {
-                            if (!parsedSound.url) parsedSound.url = parsedSound.name;
-                            if (!loadedSounds[parsedSound.url]) {
-                                loadedSound = Sound.Parse(parsedSound, scene, rootUrl);
-                                loadedSounds[parsedSound.url] = loadedSound;
-                            }
-                            else {
-                                Sound.Parse(parsedSound, scene, rootUrl, loadedSounds[parsedSound.url]);
-                            }
-                        } else {
-                            new Sound(parsedSound.name, null, scene);
-                        }
-                    }
+                if (parsedData.autoClear !== undefined && parsedData.autoClear !== null) {
+                    scene.autoClear = parsedData.autoClear;
                 }
-
-                loadedSounds = [];
-
-                // Connect parents & children and parse actions
-                for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
-                    var transformNode = scene.transformNodes[index];
-                    if (transformNode._waitingParentId) {
-                        transformNode.parent = scene.getLastEntryByID(transformNode._waitingParentId);
-                        transformNode._waitingParentId = null;
-                    }
+                if (parsedData.clearColor !== undefined && parsedData.clearColor !== null) {
+                    scene.clearColor = Color4.FromArray(parsedData.clearColor);
                 }
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    var mesh = scene.meshes[index];
-                    if (mesh._waitingParentId) {
-                        mesh.parent = scene.getLastEntryByID(mesh._waitingParentId);
-                        mesh._waitingParentId = null;
-                    }
-                    if (mesh._waitingActions) {
-                        ActionManager.Parse(mesh._waitingActions, mesh, scene);
-                        mesh._waitingActions = null;
-                    }
+                if (parsedData.ambientColor !== undefined && parsedData.ambientColor !== null) {
+                    scene.ambientColor = Color3.FromArray(parsedData.ambientColor);
                 }
-
-                // freeze world matrix application
-                for (index = 0, cache = scene.meshes.length; index < cache; index++) {
-                    var currentMesh = scene.meshes[index];
-                    if (currentMesh._waitingFreezeWorldMatrix) {
-                        currentMesh.freezeWorldMatrix();
-                        currentMesh._waitingFreezeWorldMatrix = null;
-                    } else {
-                        currentMesh.computeWorldMatrix(true);
-                    }
+                if (parsedData.gravity !== undefined && parsedData.gravity !== null) {
+                    scene.gravity = Vector3.FromArray(parsedData.gravity);
                 }
 
-                // Particles Systems
-                if (parsedData.particleSystems !== undefined && parsedData.particleSystems !== null) {
-                    for (index = 0, cache = parsedData.particleSystems.length; index < cache; index++) {
-                        var parsedParticleSystem = parsedData.particleSystems[index];
-                        ParticleSystem.Parse(parsedParticleSystem, scene, rootUrl);
+                // Fog
+                if (parsedData.fogMode && parsedData.fogMode !== 0) {
+                    scene.fogMode = parsedData.fogMode;
+                    scene.fogColor = Color3.FromArray(parsedData.fogColor);
+                    scene.fogStart = parsedData.fogStart;
+                    scene.fogEnd = parsedData.fogEnd;
+                    scene.fogDensity = parsedData.fogDensity;
+                    log += "\tFog mode for scene:  ";
+                    switch (scene.fogMode) {
+                        // getters not compiling, so using hardcoded
+                        case 1: log += "exp\n"; break;
+                        case 2: log += "exp2\n"; break;
+                        case 3: log += "linear\n"; break;
                     }
                 }
 
-                // Environment texture
-                if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {
-                    scene.environmentTexture = CubeTexture.CreateFromPrefilteredData(rootUrl + parsedData.environmentTexture, scene);
-                    if (parsedData.createDefaultSkybox === true) {
-                        var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
-                        var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        scene.createDefaultSkybox(undefined, true, skyboxScale, skyboxBlurLevel);
+                //Physics
+                if (parsedData.physicsEnabled) {
+                    var physicsPlugin;
+                    if (parsedData.physicsEngine === "cannon") {
+                        physicsPlugin = new CannonJSPlugin();
+                    } else if (parsedData.physicsEngine === "oimo") {
+                        physicsPlugin = new OimoJSPlugin();
                     }
+                    log = "\tPhysics engine " + (parsedData.physicsEngine ? parsedData.physicsEngine : "oimo") + " enabled\n";
+                    //else - default engine, which is currently oimo
+                    var physicsGravity = parsedData.physicsGravity ? Vector3.FromArray(parsedData.physicsGravity) : null;
+                    scene.enablePhysics(physicsGravity, physicsPlugin);
                 }
 
-                // Lens flares
-                if (parsedData.lensFlareSystems !== undefined && parsedData.lensFlareSystems !== null) {
-                    for (index = 0, cache = parsedData.lensFlareSystems.length; index < cache; index++) {
-                        var parsedLensFlareSystem = parsedData.lensFlareSystems[index];
-                        LensFlareSystem.Parse(parsedLensFlareSystem, scene, rootUrl);
-                    }
+                // Metadata
+                if (parsedData.metadata !== undefined && parsedData.metadata !== null) {
+                    scene.metadata = parsedData.metadata;
                 }
 
-                // Shadows
-                if (parsedData.shadowGenerators !== undefined && parsedData.shadowGenerators !== null) {
-                    for (index = 0, cache = parsedData.shadowGenerators.length; index < cache; index++) {
-                        var parsedShadowGenerator = parsedData.shadowGenerators[index];
-                        ShadowGenerator.Parse(parsedShadowGenerator, scene);
-                    }
+                //collisions, if defined. otherwise, default is true
+                if (parsedData.collisionsEnabled !== undefined && parsedData.collisionsEnabled !== null) {
+                    scene.collisionsEnabled = parsedData.collisionsEnabled;
                 }
+                scene.workerCollisions = !!parsedData.workerCollisions;
 
-                // Lights exclusions / inclusions
-                for (index = 0, cache = scene.lights.length; index < cache; index++) {
-                    let light = scene.lights[index];
-                    // Excluded check
-                    if (light._excludedMeshesIds.length > 0) {
-                        for (var excludedIndex = 0; excludedIndex < light._excludedMeshesIds.length; excludedIndex++) {
-                            var excludedMesh = scene.getMeshByID(light._excludedMeshesIds[excludedIndex]);
-
-                            if (excludedMesh) {
-                                light.excludedMeshes.push(excludedMesh);
-                            }
-                        }
-
-                        light._excludedMeshesIds = [];
-                    }
-
-                    // Included check
-                    if (light._includedOnlyMeshesIds.length > 0) {
-                        for (var includedOnlyIndex = 0; includedOnlyIndex < light._includedOnlyMeshesIds.length; includedOnlyIndex++) {
-                            var includedOnlyMesh = scene.getMeshByID(light._includedOnlyMeshesIds[includedOnlyIndex]);
-
-                            if (includedOnlyMesh) {
-                                light.includedOnlyMeshes.push(includedOnlyMesh);
-                            }
-                        }
-
-                        light._includedOnlyMeshesIds = [];
-                    }
+                var container = loadAssets(scene, data, rootUrl, onerror, true);
+                if(!container){
+                    return false;
+                }
+                
+                if (parsedData.autoAnimate) {		
+                    scene.beginAnimation(scene, parsedData.autoAnimateFrom, parsedData.autoAnimateTo, parsedData.autoAnimateLoop, parsedData.autoAnimateSpeed || 1.0);		
                 }
 
-                // Actions (scene)
-                if (parsedData.actions !== undefined && parsedData.actions !== null) {
-                    ActionManager.Parse(parsedData.actions, null, scene);
+                if (parsedData.activeCameraID !== undefined && parsedData.activeCameraID !== null) {		
+                    scene.setActiveCameraByID(parsedData.activeCameraID);		
                 }
 
+                // Environment texture		
+                if (parsedData.environmentTexture !== undefined && parsedData.environmentTexture !== null) {		
+                    scene.environmentTexture = CubeTexture.CreateFromPrefilteredData(rootUrl + parsedData.environmentTexture, scene);		
+                    if (parsedData.createDefaultSkybox === true) {		
+                        var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;		
+                        var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;		
+                        scene.createDefaultSkybox(undefined, true, skyboxScale, skyboxBlurLevel);		
+                    }
+                }
                 // Finish
                 return true;
-
             } catch (err) {
                 let msg = logOperation("importScene", parsedData ? parsedData.producer : "Unknown") + log;
                 if (onError) {
@@ -621,8 +680,11 @@
                     Tools.Log(logOperation("importScene", parsedData ? parsedData.producer : "Unknown") + (SceneLoader.loggingLevel !== SceneLoader.MINIMAL_LOGGING ? log : ""));
                 }
             }
-
             return false;
+        },
+        loadAssets: (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void): Nullable<AssetContainer> =>{
+            var container = loadAssets(scene, data, rootUrl, onerror);
+            return container;
         }
     });
 }

+ 87 - 0
src/Loading/babylon.sceneLoader.ts

@@ -27,6 +27,7 @@
         load: (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void) => boolean;
         canDirectLoad?: (data: string) => boolean;
         rewriteRootURL?: (rootUrl: string, responseURL?: string) => string;
+        loadAssets: (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void) => Nullable<AssetContainer>;
     }
 
     export interface ISceneLoaderPluginAsync {
@@ -36,6 +37,7 @@
         loadAsync: (scene: Scene, data: string, rootUrl: string, onSuccess?: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
         canDirectLoad?: (data: string) => boolean;
         rewriteRootURL?: (rootUrl: string, responseURL?: string) => string;
+        loadAssetsAsync: (scene: Scene, data: string, rootUrl: string, onSuccess?: (assets: Nullable<AssetContainer>) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string, exception?: any) => void) => void;
     }
 
     interface IRegisteredPlugin {
@@ -455,5 +457,90 @@
                 }
             }, progressHandler, errorHandler, disposeHandler, pluginExtension);
         }
+        
+        public static LoadAssetContainer(
+            rootUrl: string,
+            sceneFilename: any,
+            scene: Scene,
+            onSuccess: Nullable<(assets: AssetContainer) => void> = null,
+            onProgress: Nullable<(event: SceneLoaderProgressEvent) => void> = null,
+            onError: Nullable<(scene: Scene, message: string, exception?: any) => void> = null,
+            pluginExtension: Nullable<string> = null
+        ): Nullable<ISceneLoaderPlugin | ISceneLoaderPluginAsync> {
+            if (sceneFilename.substr && sceneFilename.substr(0, 1) === "/") {
+                Tools.Error("Wrong sceneFilename parameter");
+                return null;
+            }
+
+            var loadingToken = {};
+            scene._addPendingData(loadingToken);
+
+            var disposeHandler = () => {
+                scene._removePendingData(loadingToken);
+            };
+
+            var errorHandler = (message: Nullable<string>, exception?: any) => {
+                let errorMessage = "Unable to load assets from " + rootUrl + sceneFilename + (message ? ": " + message : "");
+                if (onError) {
+                    onError(scene, errorMessage, exception);
+                } else {
+                    Tools.Error(errorMessage);
+                    // should the exception be thrown?
+                }
+
+                disposeHandler();
+            };
+
+            var progressHandler = onProgress ? (event: SceneLoaderProgressEvent) => {
+                try {
+                    onProgress(event);
+                }
+                catch (e) {
+                    errorHandler("Error in onProgress callback", e);
+                }
+            } : undefined;
+
+            var successHandler = (assets: AssetContainer) => {
+                if (onSuccess) {
+                    try {
+                        onSuccess(assets);
+                    }
+                    catch (e) {
+                        errorHandler("Error in onSuccess callback", e);
+                    }
+                }
+
+                scene._removePendingData(loadingToken);
+            };
+
+            return SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data, responseURL) => {
+                if ((<any>plugin).loadAssets) {
+                    var syncedPlugin = <ISceneLoaderPlugin>plugin;
+                    var assetContainer = syncedPlugin.loadAssets(scene, data, rootUrl, errorHandler);
+                    if (!assetContainer) {
+                        return;
+                    }
+
+                    scene.loadingPluginName = plugin.name;
+                    successHandler(assetContainer);
+                } else if ((<any>plugin).loadAssetsAsync) {
+                    var asyncedPlugin = <ISceneLoaderPluginAsync>plugin;
+                    asyncedPlugin.loadAssetsAsync(scene, data, rootUrl, (assetContainer) => {
+                        if(assetContainer){
+                            scene.loadingPluginName = plugin.name;
+                            successHandler(assetContainer);
+                        }
+                    }, progressHandler, errorHandler);
+                }else{
+                    errorHandler("LoadAssetContainer is not supported by this plugin. Plugin did not provide a loadAssets or loadAssetsAsync method.")
+                }
+
+                if (SceneLoader.ShowLoadingScreen) {
+                    scene.executeWhenReady(() => {
+                        scene.getEngine().hideLoadingUI();
+                    });
+                }
+            }, progressHandler, errorHandler, disposeHandler, pluginExtension);
+        }
     };
 }

+ 0 - 280
src/Math/babylon.math.SIMD.ts

@@ -1,280 +0,0 @@
-module BABYLON {
-
-    declare var SIMD: SIMD;
-
-    class SIMDVector3 {
-        public static TransformCoordinatesToRefSIMD(vector: Vector3, transformation: Matrix, result: Vector3): void {
-            SIMDVector3.TransformCoordinatesFromFloatsToRefSIMD(vector.x, vector.y, vector.z, transformation, result);
-        }
-
-        public static TransformCoordinatesFromFloatsToRefSIMD(x: number, y: number, z: number, transformation: Matrix, result: Vector3): void {
-            var m = transformation.m;
-
-            var m0 = SIMD.Float32x4.load(m, 0);
-            var m1 = SIMD.Float32x4.load(m, 4);
-            var m2 = SIMD.Float32x4.load(m, 8);
-            var m3 = SIMD.Float32x4.load(m, 12);
-            var r = SIMD.Float32x4.add(
-                SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.splat(x), m0), SIMD.Float32x4.mul(SIMD.Float32x4.splat(y), m1)),
-                SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.splat(z), m2), m3)
-            );
-            r = SIMD.Float32x4.div(r, SIMD.Float32x4.swizzle(r, 3, 3, 3, 3));
-            result.x = SIMD.Float32x4.extractLane(r, 0);
-            result.y = SIMD.Float32x4.extractLane(r, 1);
-            result.z = SIMD.Float32x4.extractLane(r, 2);
-        }
-    }
-
-    class SIMDMatrix {
-        public multiplyToArraySIMD(other: Matrix, result: Float32Array, offset: number): Matrix {
-            var tm: Float32Array = (<any>this).m;
-            var om = other.m;
-
-            var m0 = SIMD.Float32x4.load(om, 0);
-            var m1 = SIMD.Float32x4.load(om, 4);
-            var m2 = SIMD.Float32x4.load(om, 8);
-            var m3 = SIMD.Float32x4.load(om, 12);
-
-            for (var i = 0; i < 16; i += 4) {
-                SIMD.Float32x4.store(result, i + offset, SIMD.Float32x4.add(
-                    SIMD.Float32x4.mul(SIMD.Float32x4.splat(tm[i]), m0),
-                    SIMD.Float32x4.add(
-                        SIMD.Float32x4.mul(SIMD.Float32x4.splat(tm[i + 1]), m1),
-                        SIMD.Float32x4.add(
-                            SIMD.Float32x4.mul(SIMD.Float32x4.splat(tm[i + 2]), m2),
-                            SIMD.Float32x4.mul(SIMD.Float32x4.splat(tm[i + 3]), m3)))));
-            }
-
-            return (<any>this);
-        }
-
-        public invertToRefSIMD(other: Matrix): Matrix {
-            var src: Float32Array = (<any>this).m;
-            var dest = other.m;
-
-            // Load the 4 rows
-            var src0 = SIMD.Float32x4.load(src, 0);
-            var src1 = SIMD.Float32x4.load(src, 4);
-            var src2 = SIMD.Float32x4.load(src, 8);
-            var src3 = SIMD.Float32x4.load(src, 12);
-
-            // Transpose the source matrix.  Sort of.  Not a true transpose operation
-
-            var tmp1 = SIMD.Float32x4.shuffle(src0, src1, 0, 1, 4, 5);
-            var row1 = SIMD.Float32x4.shuffle(src2, src3, 0, 1, 4, 5);
-            var row0 = SIMD.Float32x4.shuffle(tmp1, row1, 0, 2, 4, 6);
-            row1 = SIMD.Float32x4.shuffle(row1, tmp1, 1, 3, 5, 7);
-
-            tmp1 = SIMD.Float32x4.shuffle(src0, src1, 2, 3, 6, 7);
-            var row3 = SIMD.Float32x4.shuffle(src2, src3, 2, 3, 6, 7);
-            var row2 = SIMD.Float32x4.shuffle(tmp1, row3, 0, 2, 4, 6);
-            row3 = SIMD.Float32x4.shuffle(row3, tmp1, 1, 3, 5, 7);
-
-            // This is a true transposition, but it will lead to an incorrect result
-
-            //tmp1 = shuffle(src0, src1, 0, 1, 4, 5);
-            //tmp2 = shuffle(src2, src3, 0, 1, 4, 5);
-            //row0  = shuffle(tmp1, tmp2, 0, 2, 4, 6);
-            //row1  = shuffle(tmp1, tmp2, 1, 3, 5, 7);
-
-            //tmp1 = shuffle(src0, src1, 2, 3, 6, 7);
-            //tmp2 = shuffle(src2, src3, 2, 3, 6, 7);
-            //row2  = shuffle(tmp1, tmp2, 0, 2, 4, 6);
-            //row3  = shuffle(tmp1, tmp2, 1, 3, 5, 7);
-
-            // ----
-            tmp1 = SIMD.Float32x4.mul(row2, row3);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001
-            var minor0 = SIMD.Float32x4.mul(row1, tmp1);
-            var minor1 = SIMD.Float32x4.mul(row0, tmp1);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110
-            minor0 = SIMD.Float32x4.sub(SIMD.Float32x4.mul(row1, tmp1), minor0);
-            minor1 = SIMD.Float32x4.sub(SIMD.Float32x4.mul(row0, tmp1), minor1);
-            minor1 = SIMD.Float32x4.swizzle(minor1, 2, 3, 0, 1); // 0x4E = 01001110
-
-            // ----
-            tmp1 = SIMD.Float32x4.mul(row1, row2);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001
-            minor0 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row3, tmp1), minor0);
-            var minor3 = SIMD.Float32x4.mul(row0, tmp1);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110
-            minor0 = SIMD.Float32x4.sub(minor0, SIMD.Float32x4.mul(row3, tmp1));
-            minor3 = SIMD.Float32x4.sub(SIMD.Float32x4.mul(row0, tmp1), minor3);
-            minor3 = SIMD.Float32x4.swizzle(minor3, 2, 3, 0, 1); // 0x4E = 01001110
-
-            // ----
-            tmp1 = SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(row1, 2, 3, 0, 1), row3); // 0x4E = 01001110
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001
-            row2 = SIMD.Float32x4.swizzle(row2, 2, 3, 0, 1);  // 0x4E = 01001110
-            minor0 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row2, tmp1), minor0);
-            var minor2 = SIMD.Float32x4.mul(row0, tmp1);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110
-            minor0 = SIMD.Float32x4.sub(minor0, SIMD.Float32x4.mul(row2, tmp1));
-            minor2 = SIMD.Float32x4.sub(SIMD.Float32x4.mul(row0, tmp1), minor2);
-            minor2 = SIMD.Float32x4.swizzle(minor2, 2, 3, 0, 1); // 0x4E = 01001110
-
-            // ----
-            tmp1 = SIMD.Float32x4.mul(row0, row1);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001
-            minor2 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row3, tmp1), minor2);
-            minor3 = SIMD.Float32x4.sub(SIMD.Float32x4.mul(row2, tmp1), minor3);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110
-            minor2 = SIMD.Float32x4.sub(SIMD.Float32x4.mul(row3, tmp1), minor2);
-            minor3 = SIMD.Float32x4.sub(minor3, SIMD.Float32x4.mul(row2, tmp1));
-
-            // ----
-            tmp1 = SIMD.Float32x4.mul(row0, row3);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001
-            minor1 = SIMD.Float32x4.sub(minor1, SIMD.Float32x4.mul(row2, tmp1));
-            minor2 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row1, tmp1), minor2);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110
-            minor1 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row2, tmp1), minor1);
-            minor2 = SIMD.Float32x4.sub(minor2, SIMD.Float32x4.mul(row1, tmp1));
-
-            // ----
-            tmp1 = SIMD.Float32x4.mul(row0, row2);
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 1, 0, 3, 2); // 0xB1 = 10110001
-            minor1 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row3, tmp1), minor1);
-            minor3 = SIMD.Float32x4.sub(minor3, SIMD.Float32x4.mul(row1, tmp1));
-            tmp1 = SIMD.Float32x4.swizzle(tmp1, 2, 3, 0, 1); // 0x4E = 01001110
-            minor1 = SIMD.Float32x4.sub(minor1, SIMD.Float32x4.mul(row3, tmp1));
-            minor3 = SIMD.Float32x4.add(SIMD.Float32x4.mul(row1, tmp1), minor3);
-
-            // Compute determinant
-            var det = SIMD.Float32x4.mul(row0, minor0);
-            det = SIMD.Float32x4.add(SIMD.Float32x4.swizzle(det, 2, 3, 0, 1), det); // 0x4E = 01001110
-            det = SIMD.Float32x4.add(SIMD.Float32x4.swizzle(det, 1, 0, 3, 2), det); // 0xB1 = 10110001
-            tmp1 = SIMD.Float32x4.reciprocalApproximation(det);
-            det = SIMD.Float32x4.sub(SIMD.Float32x4.add(tmp1, tmp1), SIMD.Float32x4.mul(det, SIMD.Float32x4.mul(tmp1, tmp1)));
-            det = SIMD.Float32x4.swizzle(det, 0, 0, 0, 0);
-
-            // These shuffles aren't necessary if the faulty transposition is done
-            // up at the top of this function.
-            //minor0 =SIMD.Float32x4.swizzle(minor0, 2, 1, 0, 3);
-            //minor1 =SIMD.Float32x4.swizzle(minor1, 2, 1, 0, 3);
-            //minor2 =SIMD.Float32x4.swizzle(minor2, 2, 1, 0, 3);
-            //minor3 =SIMD.Float32x4.swizzle(minor3, 2, 1, 0, 3);
-
-            // Compute final values by multiplying with 1/det
-            SIMD.Float32x4.store(dest, 0, SIMD.Float32x4.mul(det, minor0));
-            SIMD.Float32x4.store(dest, 4, SIMD.Float32x4.mul(det, minor1));
-            SIMD.Float32x4.store(dest, 8, minor2 = SIMD.Float32x4.mul(det, minor2));
-            SIMD.Float32x4.store(dest, 12, SIMD.Float32x4.mul(det, minor3));
-
-            return (<any>this);
-        }
-
-        public static LookAtLHToRefSIMD(eyeRef: Vector3, targetRef: Vector3, upRef: Vector3, result: Matrix): void {
-            var out = result.m;
-            var center = SIMD.Float32x4(targetRef.x, targetRef.y, targetRef.z, 0.0);
-            var eye = SIMD.Float32x4(eyeRef.x, eyeRef.y, eyeRef.z, 0.0);
-            var up = SIMD.Float32x4(upRef.x, upRef.y, upRef.z, 0.0);
-
-            // cc.kmVec3Subtract(f, pCenter, pEye);
-            var f = SIMD.Float32x4.sub(center, eye);
-            // cc.kmVec3Normalize(f, f);    
-            var tmp = SIMD.Float32x4.mul(f, f);
-            tmp = SIMD.Float32x4.add(tmp, SIMD.Float32x4.add(SIMD.Float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.Float32x4.swizzle(tmp, 2, 0, 1, 3)));
-            f = SIMD.Float32x4.mul(f, SIMD.Float32x4.reciprocalSqrtApproximation(tmp));
-
-            // cc.kmVec3Assign(up, pUp);
-            // cc.kmVec3Normalize(up, up);
-            tmp = SIMD.Float32x4.mul(up, up);
-            tmp = SIMD.Float32x4.add(tmp, SIMD.Float32x4.add(SIMD.Float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.Float32x4.swizzle(tmp, 2, 0, 1, 3)));
-            up = SIMD.Float32x4.mul(up, SIMD.Float32x4.reciprocalSqrtApproximation(tmp));
-            // cc.kmVec3Cross(s, f, up);
-            var s = SIMD.Float32x4.sub(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(f, 1, 2, 0, 3), SIMD.Float32x4.swizzle(up, 2, 0, 1, 3)), SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(f, 2, 0, 1, 3), SIMD.Float32x4.swizzle(up, 1, 2, 0, 3)));
-            // cc.kmVec3Normalize(s, s);
-            tmp = SIMD.Float32x4.mul(s, s);
-            tmp = SIMD.Float32x4.add(tmp, SIMD.Float32x4.add(SIMD.Float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.Float32x4.swizzle(tmp, 2, 0, 1, 3)));
-            s = SIMD.Float32x4.mul(s, SIMD.Float32x4.reciprocalSqrtApproximation(tmp));
-            // cc.kmVec3Cross(u, s, f);
-            var u = SIMD.Float32x4.sub(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(s, 1, 2, 0, 3), SIMD.Float32x4.swizzle(f, 2, 0, 1, 3)), SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(s, 2, 0, 1, 3), SIMD.Float32x4.swizzle(f, 1, 2, 0, 3)));
-            // cc.kmVec3Normalize(s, s);
-            tmp = SIMD.Float32x4.mul(s, s);
-            tmp = SIMD.Float32x4.add(tmp, SIMD.Float32x4.add(SIMD.Float32x4.swizzle(tmp, 1, 2, 0, 3), SIMD.Float32x4.swizzle(tmp, 2, 0, 1, 3)));
-            s = SIMD.Float32x4.mul(s, SIMD.Float32x4.reciprocalSqrtApproximation(tmp));
-
-            var zero = SIMD.Float32x4.splat(0.0);
-            s = SIMD.Float32x4.neg(s);
-            var tmp01 = SIMD.Float32x4.shuffle(s, u, 0, 1, 4, 5);
-            var tmp23 = SIMD.Float32x4.shuffle(f, zero, 0, 1, 4, 5);
-            var a0 = SIMD.Float32x4.shuffle(tmp01, tmp23, 0, 2, 4, 6);
-            var a1 = SIMD.Float32x4.shuffle(tmp01, tmp23, 1, 3, 5, 7);
-            var a2 = SIMD.Float32x4.shuffle(SIMD.Float32x4.shuffle(s, u, 2, 3, 6, 7), SIMD.Float32x4.shuffle(f, zero, 2, 3, 6, 7), 0, 2, 4, 6);
-            var a3 = SIMD.Float32x4(0.0, 0.0, 0.0, 1.0);
-
-            var b = SIMD.Float32x4(1.0, 0.0, 0.0, 0.0);
-            SIMD.Float32x4.store(out, 0, SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 0, 0, 0, 0), a0), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 1, 1, 1, 1), a1), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 2, 2, 2, 2), a2), SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 3, 3, 3, 3), a3)))));
-
-            b = SIMD.Float32x4(0.0, 1.0, 0.0, 0.0);
-            SIMD.Float32x4.store(out, 4, SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 0, 0, 0, 0), a0), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 1, 1, 1, 1), a1), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 2, 2, 2, 2), a2), SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 3, 3, 3, 3), a3)))));
-
-            b = SIMD.Float32x4(0.0, 0.0, 1.0, 0.0);
-            SIMD.Float32x4.store(out, 8, SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 0, 0, 0, 0), a0), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 1, 1, 1, 1), a1), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 2, 2, 2, 2), a2), SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 3, 3, 3, 3), a3)))));
-
-            b = SIMD.Float32x4.replaceLane(SIMD.Float32x4.neg(eye), 3, 1.0);
-            SIMD.Float32x4.store(out, 12, SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 0, 0, 0, 0), a0), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 1, 1, 1, 1), a1), SIMD.Float32x4.add(SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 2, 2, 2, 2), a2), SIMD.Float32x4.mul(SIMD.Float32x4.swizzle(b, 3, 3, 3, 3), a3)))));
-
-        }
-    }
-
-    var previousMultiplyToArray = Matrix.prototype.multiplyToArray;
-    var previousInvertToRef = Matrix.prototype.invertToRef;
-    var previousLookAtLHToRef = Matrix.LookAtLHToRef;
-    var previousTransformCoordinatesToRef = Vector3.TransformCoordinatesToRef;
-    var previousTransformCoordinatesFromFloatsToRef = Vector3.TransformCoordinatesFromFloatsToRef;
-
-    export class SIMDHelper {
-        private static _isEnabled = false;
-
-        public static get IsEnabled(): boolean {
-            return SIMDHelper._isEnabled;
-        }
-
-        public static DisableSIMD(): void {
-            // Replace functions
-            Matrix.prototype.multiplyToArray = previousMultiplyToArray;
-            Matrix.prototype.invertToRef = previousInvertToRef;
-            Matrix.LookAtLHToRef = previousLookAtLHToRef;
-            Vector3.TransformCoordinatesToRef = previousTransformCoordinatesToRef;
-            Vector3.TransformCoordinatesFromFloatsToRef = previousTransformCoordinatesFromFloatsToRef;
-
-            SIMDHelper._isEnabled = false;
-        }
-
-        public static EnableSIMD(): void {
-            if (self.SIMD === undefined) {
-                return;
-            }
-
-            // check if polyfills needed
-            if (!self.Math.fround) {
-                self.Math.fround = (array => (x: number) => {
-                    return array[0] = x, array[0];
-                })(new Float32Array(1));
-            }
-
-            if (!self.Math.imul) {
-                self.Math.imul = (a, b) => {
-                    var ah = (a >>> 16) & 0xffff;
-                    var al = a & 0xffff;
-                    var bh = (b >>> 16) & 0xffff;
-                    var bl = b & 0xffff;
-                    // the shift by 0 fixes the sign on the high part
-                    // the final |0 converts the unsigned value into a signed value
-                    return ((al * bl) + (((ah * bl + al * bh) << 16) >>> 0) | 0);
-                };
-            }
-
-            // Replace functions
-            Matrix.prototype.multiplyToArray = SIMDMatrix.prototype.multiplyToArraySIMD;
-            Matrix.prototype.invertToRef = SIMDMatrix.prototype.invertToRefSIMD;
-            Matrix.LookAtLHToRef = SIMDMatrix.LookAtLHToRefSIMD;
-            Vector3.TransformCoordinatesToRef = SIMDVector3.TransformCoordinatesToRefSIMD;
-            Vector3.TransformCoordinatesFromFloatsToRef = SIMDVector3.TransformCoordinatesFromFloatsToRefSIMD;
-
-            SIMDHelper._isEnabled = true;
-        }
-    }
-}

+ 13 - 5
src/Mesh/babylon.instancedMesh.ts

@@ -35,9 +35,9 @@
          */
         public getClassName(): string {
             return "InstancedMesh";
-        }          
+        }
 
-        // Methods
+        // Methods      
         public get receiveShadows(): boolean {
             return this._sourceMesh.receiveShadows;
         }
@@ -70,6 +70,14 @@
         }
 
         /**
+         * Is this node ready to be used/rendered
+         * @return {boolean} is it ready
+         */
+        public isReady(): boolean {
+            return this._sourceMesh.isReady(true);
+        }
+
+        /**
          * Returns a float array or a Float32Array of the requested kind of data : positons, normals, uvs, etc.  
          */
         public getVerticesData(kind: string, copyWhenShared?: boolean): Nullable<FloatArray> {
@@ -103,7 +111,7 @@
          */
         public setVerticesData(kind: string, data: FloatArray, updatable?: boolean, stride?: number): Mesh {
             if (this.sourceMesh) {
-               this.sourceMesh.setVerticesData(kind, data, updatable, stride);
+                this.sourceMesh.setVerticesData(kind, data, updatable, stride);
             }
             return this.sourceMesh;
         }
@@ -134,7 +142,7 @@
          */
         public updateVerticesData(kind: string, data: FloatArray, updateExtends?: boolean, makeItUnique?: boolean): Mesh {
             if (this.sourceMesh) {
-               this.sourceMesh.updateVerticesData(kind, data, updateExtends, makeItUnique);
+                this.sourceMesh.updateVerticesData(kind, data, updateExtends, makeItUnique);
             }
             return this.sourceMesh;
         }
@@ -148,7 +156,7 @@
          */
         public setIndices(indices: IndicesArray, totalVertices: Nullable<number> = null): Mesh {
             if (this.sourceMesh) {
-               this.sourceMesh.setIndices(indices, totalVertices);
+                this.sourceMesh.setIndices(indices, totalVertices);
             }
             return this.sourceMesh;
         }

+ 68 - 6
src/Mesh/babylon.mesh.ts

@@ -191,10 +191,10 @@
                 // Metadata
                 if (source.metadata && source.metadata.clone) {
                     this.metadata = source.metadata.clone();
-                 } else {
+                } else {
                     this.metadata = source.metadata;
-                 }                
-                
+                }
+
                 // Tags
                 if (Tags && Tags.HasTags(source)) {
                     Tags.AddTagsTo(this, Tags.GetTags(source, true));
@@ -598,14 +598,76 @@
         }
 
         /**
-         * Boolean : true once the mesh is ready after all the delayed process (loading, etc) are complete.
+         * Determine if the current mesh is ready to be rendered
+         * @param forceInstanceSupport will check if the mesh will be ready when used with instances (false by default)
+         * @returns true if all associated assets are ready (material, textures, shaders)
          */
-        public isReady(): boolean {
+        public isReady(forceInstanceSupport = false): boolean {
             if (this.delayLoadState === Engine.DELAYLOADSTATE_LOADING) {
                 return false;
             }
 
-            return super.isReady();
+            if (!super.isReady()) {
+                return false;
+            }
+
+            if (!this.subMeshes || this.subMeshes.length === 0) {
+                return true;
+            }
+
+            let engine = this.getEngine();
+            let scene = this.getScene();
+            let hardwareInstancedRendering = forceInstanceSupport || engine.getCaps().instancedArrays && this.instances.length > 0;
+
+            this.computeWorldMatrix();
+
+            let mat = this.material || scene.defaultMaterial;
+            if (mat) {
+                let currentAlphaTestingState = engine.getAlphaTesting();
+
+                if (mat.storeEffectOnSubMeshes) {
+                    for (var subMesh of this.subMeshes) {
+                        let effectiveMaterial = subMesh.getMaterial();
+                        if (effectiveMaterial) {
+                            engine.setAlphaTesting(effectiveMaterial.needAlphaTesting() && !effectiveMaterial.needAlphaBlendingForMesh(this));
+                            if (!effectiveMaterial.isReadyForSubMesh(this, subMesh, hardwareInstancedRendering)) {
+                                engine.setAlphaTesting(currentAlphaTestingState);
+                                return false;
+                            }
+                        }
+                    }
+                } else {
+                    engine.setAlphaTesting(mat.needAlphaTesting() && !mat.needAlphaBlendingForMesh(this));
+                    if (!mat.isReady(this, hardwareInstancedRendering)) {
+                        engine.setAlphaTesting(currentAlphaTestingState);
+                        return false;
+                    }
+                }
+
+                engine.setAlphaTesting(currentAlphaTestingState);
+            }
+
+            // Shadows
+            for (var light of this._lightSources) {
+                let generator = light.getShadowGenerator();
+
+                if (generator) {
+                    for (var subMesh of this.subMeshes) {
+                        if (!generator.isReady(subMesh, hardwareInstancedRendering)) {
+                            return false;
+                        }
+                    }
+                }
+            }
+
+            // LOD
+            for (var lod of this._LODLevels) {
+                if (lod.mesh && !lod.mesh.isReady(hardwareInstancedRendering)) {
+                    return false;
+                }
+            }
+
+            return true;
         }
 
         /**

+ 0 - 3
src/Mesh/babylon.transformNode.ts

@@ -118,9 +118,6 @@ module BABYLON {
          * Returns the latest update of the World matrix determinant.
          */
         protected _getWorldMatrixDeterminant(): number {
-            if (this._currentRenderId !== this.getScene().getRenderId()) {
-                this.computeWorldMatrix();
-            }
             return this._worldMatrixDeterminant;
         }
 

+ 4 - 9
src/Morph/babylon.morphTargetManager.ts

@@ -103,19 +103,14 @@ module BABYLON {
             this._supportsTangents = true;
             this._vertexCount = 0;
             for (var target of this._targets) {
-                if (target.influence > 0) {
-                    this._activeTargets.push(target);
-                    this._tempInfluences[influenceCount++] = target.influence;
+                this._activeTargets.push(target);
+                this._tempInfluences[influenceCount++] = target.influence;
 
+                const positions = target.getPositions();
+                if (positions) {
                     this._supportsNormals = this._supportsNormals && target.hasNormals;
                     this._supportsTangents = this._supportsTangents && target.hasTangents;
 
-                    const positions = target.getPositions();
-                    if (!positions) {
-                        Tools.Error("Invalid target. Target must positions.");
-                        return;
-                    }
-
                     const vertexCount = positions.length / 3;
                     if (this._vertexCount === 0) {
                         this._vertexCount = vertexCount;

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

@@ -174,7 +174,7 @@
                 this._uvs32 = new Float32Array(this._uvs);
                 this._colors32 = new Float32Array(this._colors);
                 if (this.recomputeNormals) {
-                    VertexData.ComputeNormals(this._positions32, this._indices, this._normals);
+                    VertexData.ComputeNormals(this._positions32, this._indices32, this._normals);
                 }
                 this._normals32 = new Float32Array(this._normals);
                 this._fixedNormal32 = new Float32Array(this._normals);
@@ -899,7 +899,7 @@
                         if (this._computeParticleVertex || this.mesh.isFacetDataEnabled) {
                             // recompute the normals only if the particles can be morphed, update then also the normal reference array _fixedNormal32[]
                             var params = this.mesh.isFacetDataEnabled ? this.mesh.getFacetDataParameters() : null;
-                            VertexData.ComputeNormals(this._positions32, this._indices, this._normals32, params);
+                            VertexData.ComputeNormals(this._positions32, this._indices32, this._normals32, params);
                             for (var i = 0; i < this._normals32.length; i++) {
                                 this._fixedNormal32[i] = this._normals32[i];
                             }                       

+ 8 - 0
src/PostProcess/babylon.postProcess.ts

@@ -334,6 +334,14 @@
             return this.width / this.height;
         }
 
+        /**
+         * Get a value indicating if the post-process is ready to be used
+         * @returns true if the post-process is ready (shader is compiled)
+         */
+        public isReady(): boolean {
+            return this._effect && this._effect.isReady();
+        }
+
         public apply(): Nullable<Effect> {
             // Check
             if (!this._effect || !this._effect.isReady())

+ 2 - 2
src/PostProcess/babylon.volumetricLightScatteringPostProcess.ts

@@ -135,7 +135,7 @@
             return "VolumetricLightScatteringPostProcess";
         }
 
-        public isReady(subMesh: SubMesh, useInstances: boolean): boolean {
+        private _isReady(subMesh: SubMesh, useInstances: boolean): boolean {
             var mesh = subMesh.getMesh();
 
             // Render this.mesh as default
@@ -287,7 +287,7 @@
 
                 var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null);
 
-                if (this.isReady(subMesh, hardwareInstancedRendering)) {
+                if (this._isReady(subMesh, hardwareInstancedRendering)) {
                     var effect: Effect = this._volumetricLightScatteringPass;
                     if (mesh === this.mesh) {
                         if (subMesh.effect) {

+ 486 - 63
src/Tools/babylon.sceneOptimizer.ts

@@ -1,20 +1,61 @@
 module BABYLON {
-    // Standard optimizations
+    /**
+     * Defines the root class used to create scene optimization to use with SceneOptimizer
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class SceneOptimization {
-        public apply = (scene: Scene): boolean => {
-            return true; // Return true if everything that can be done was applied
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         * @returnstrue if everything that can be done was applied
+         */
+        public apply(scene: Scene): boolean {
+            return true;
         };
 
+        /**
+         * Creates the SceneOptimization object
+         * @param priority defines the priority of this optimization (0 by default which means first in the list)
+         * @param desc defines the description associated with the optimization
+         */
         constructor(public priority: number = 0) {
         }
     }
 
+    /**
+     * Defines an optimization used to reduce the size of render target textures
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class TextureOptimization extends SceneOptimization {
-        constructor(public priority: number = 0, public maximumSize: number = 1024) {
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Reducing render target texture size to " + this.maximumSize;
+        }
+
+        /**
+         * Creates the TextureOptimization object
+         * @param priority defines the priority of this optimization (0 by default which means first in the list)
+         * @param maximumSize defines the maximum sized allowed for textures (1024 is the default value). If a texture is bigger, it will be scaled down using a factor defined by the step parameter
+         * @param step defines the factor (0.5 by default) used to scale down textures bigger than maximum sized allowed.
+         */
+        constructor(public priority: number = 0, public maximumSize: number = 1024, public step = 0.5) {
             super(priority);
         }
 
-        public apply = (scene: Scene): boolean => {
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
 
             var allDone = true;
             for (var index = 0; index < scene.textures.length; index++) {
@@ -28,7 +69,7 @@
                 var maxDimension = Math.max(currentSize.width, currentSize.height);
 
                 if (maxDimension > this.maximumSize) {
-                    texture.scale(0.5);
+                    texture.scale(this.step);
                     allDone = false;
                 }
             }
@@ -37,68 +78,230 @@
         }
     }
 
+    /**
+     * Defines an optimization used to increase or decrease the rendering resolution
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class HardwareScalingOptimization extends SceneOptimization {
-        private _currentScale = 1;
+        private _currentScale = -1;
+        private _directionOffset = 1;
+
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Setting hardware scaling level to " + this._currentScale;
+        }
 
-        constructor(public priority: number = 0, public maximumScale: number = 2) {
+        /**
+         * Creates the HardwareScalingOptimization object
+         * @param priority priority defines the priority of this optimization (0 by default which means first in the list)
+         * @param maximumScale 
+         * @param step 
+         */
+        constructor(public priority: number = 0, public maximumScale: number = 2, public step: number = 0.25) {
             super(priority);
         }
 
-        public apply = (scene: Scene): boolean => {
-            this._currentScale++;
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
+            if (this._currentScale === -1) {
+                this._currentScale = scene.getEngine().getHardwareScalingLevel();
+                if (this._currentScale > this.maximumScale) {
+                    this._directionOffset = -1;
+                }
+            }
+
+            this._currentScale += this._directionOffset * this.step;
 
             scene.getEngine().setHardwareScalingLevel(this._currentScale);
 
-            return this._currentScale >= this.maximumScale;
+            return this._directionOffset === 1 ? this._currentScale >= this.maximumScale : this._currentScale <= this.maximumScale;
         };
     }
 
+    /**
+     * Defines an optimization used to remove shadows
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class ShadowsOptimization extends SceneOptimization {
-        public apply = (scene: Scene): boolean => {
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Turning shadows off";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
             scene.shadowsEnabled = false;
             return true;
         };
     }
 
+    /**
+     * Defines an optimization used to turn post-processes off
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class PostProcessesOptimization extends SceneOptimization {
-        public apply = (scene: Scene): boolean => {
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Turning post-processes off";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
             scene.postProcessesEnabled = false;
             return true;
         };
     }
 
+    /**
+     * Defines an optimization used to turn lens flares off
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class LensFlaresOptimization extends SceneOptimization {
-        public apply = (scene: Scene): boolean => {
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Turning lens flares off";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
             scene.lensFlaresEnabled = false;
             return true;
         };
     }
 
+    /**
+     * Defines an optimization based on user defined callback.
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
+    export class CustomOptimization extends SceneOptimization {
+        /**
+         * Callback called to apply the custom optimization.
+         * @param scene defines the scene to apply the optimization to
+         * @returns a boolean defining if all the optimization was applied or if more options are available (for the next turn)
+         */
+        public onApply: (scene: Scene) => boolean;
+
+        /**
+         * Callback called to get custom description
+         */
+        public onGetDescription: () => string;
+
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            if (this.onGetDescription) {
+                return this.onGetDescription();
+            }
+
+            return "Running user defined callback";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
+            if (this.onApply) {
+                return this.onApply(scene);
+            }
+            return true;
+        };
+    }
+
+    /**
+     * Defines an optimization used to turn particles off
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class ParticlesOptimization extends SceneOptimization {
-        public apply = (scene: Scene): boolean => {
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Turning particles off";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
             scene.particlesEnabled = false;
             return true;
         };
     }
 
+    /**
+     * Defines an optimization used to turn render targets off
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class RenderTargetsOptimization extends SceneOptimization {
-        public apply = (scene: Scene): boolean => {
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Turning render targets off";
+        }
+
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene): boolean {
             scene.renderTargetsEnabled = false;
             return true;
         };
     }
 
+
+    /**
+     * Defines an optimization used to merge meshes with compatible materials
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class MergeMeshesOptimization extends SceneOptimization {
         static _UpdateSelectionTree = false;
 
+        /**
+         * Gets or sets a boolean which defines if optimization octree has to be updated
+         */
         public static get UpdateSelectionTree(): boolean {
             return MergeMeshesOptimization._UpdateSelectionTree;
         }
 
+        /**
+         * Gets or sets a boolean which defines if optimization octree has to be updated
+         */
         public static set UpdateSelectionTree(value: boolean) {
             MergeMeshesOptimization._UpdateSelectionTree = value;
         }
 
+        /**
+         * Gets a string describing the action executed by the current optimization
+         */
+        public getDescription(): string {
+            return "Merging similar meshes together";
+        }
+
         private _canBeMerged = (abstractMesh: AbstractMesh): boolean => {
             if (!(abstractMesh instanceof Mesh)) {
                 return false;
@@ -125,7 +328,11 @@
             return true;
         }
 
-        public apply = (scene: Scene, updateSelectionTree?: boolean): boolean => {
+        /**
+         * This function will be called by the SceneOptimizer when its priority is reached in order to apply the change required by the current optimization
+         * @param scene defines the current scene where to apply this optimization
+         */
+        public apply(scene: Scene, updateSelectionTree?: boolean): boolean {
 
             var globalPool = scene.meshes.slice(0);
             var globalLength = globalPool.length;
@@ -186,101 +393,290 @@
         };
     }
 
-    // Options
+    /**
+     * Defines a list of options used by SceneOptimizer
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
     export class SceneOptimizerOptions {
+        /**
+         * Gets the list of optimizations to apply
+         */
         public optimizations = new Array<SceneOptimization>();
 
+        /**
+         * Creates a new list of options used by SceneOptimizer
+         * @param targetFrameRate defines the target frame rate to reach (60 by default)
+         * @param trackerDuration defines the interval between two checkes (2000ms by default)
+         */
         constructor(public targetFrameRate: number = 60, public trackerDuration: number = 2000) {
         }
 
+        /**
+         * Add a new optimization
+         * @param optimization defines the SceneOptimization to add to the list of active optimizations
+         */
+        public addOptimization(optimization: SceneOptimization): SceneOptimizerOptions {
+            this.optimizations.push(optimization);
+            return this;
+        }
+
+        /**
+         * Add a new custom optimization
+         * @param onApply defines the callback called to apply the custom optimization.
+         * @param onGetDescription defines the callback called to get the description attached with the optimization.
+         * @param priority defines the priority of this optimization (0 by default which means first in the list)
+         */
+        public addCustomOptimization(onApply: (scene: Scene) => boolean, onGetDescription: () => string, priority: number = 0): SceneOptimizerOptions {
+            let optimization = new CustomOptimization(priority);
+            optimization.onApply = onApply;
+            optimization.onGetDescription = onGetDescription;
+
+            this.optimizations.push(optimization);
+            return this;
+        }
+
+        /**
+         * Creates a list of pre-defined optimizations aimed to reduce the visual impact on the scene
+         * @param targetFrameRate defines the target frame rate (60 by default)
+         * @returns a SceneOptimizerOptions object
+         */
         public static LowDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions {
             var result = new SceneOptimizerOptions(targetFrameRate);
 
             var priority = 0;
-            result.optimizations.push(new MergeMeshesOptimization(priority));
-            result.optimizations.push(new ShadowsOptimization(priority));
-            result.optimizations.push(new LensFlaresOptimization(priority));
+            result.addOptimization(new MergeMeshesOptimization(priority));
+            result.addOptimization(new ShadowsOptimization(priority));
+            result.addOptimization(new LensFlaresOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new PostProcessesOptimization(priority));
-            result.optimizations.push(new ParticlesOptimization(priority));
+            result.addOptimization(new PostProcessesOptimization(priority));
+            result.addOptimization(new ParticlesOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new TextureOptimization(priority, 1024));
+            result.addOptimization(new TextureOptimization(priority, 1024));
 
             return result;
         }
 
+        /**
+         * Creates a list of pre-defined optimizations aimed to have a moderate impact on the scene visual
+         * @param targetFrameRate defines the target frame rate (60 by default)
+         * @returns a SceneOptimizerOptions object
+         */
         public static ModerateDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions {
             var result = new SceneOptimizerOptions(targetFrameRate);
 
             var priority = 0;
-            result.optimizations.push(new MergeMeshesOptimization(priority));
-            result.optimizations.push(new ShadowsOptimization(priority));
-            result.optimizations.push(new LensFlaresOptimization(priority));
+            result.addOptimization(new MergeMeshesOptimization(priority));
+            result.addOptimization(new ShadowsOptimization(priority));
+            result.addOptimization(new LensFlaresOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new PostProcessesOptimization(priority));
-            result.optimizations.push(new ParticlesOptimization(priority));
+            result.addOptimization(new PostProcessesOptimization(priority));
+            result.addOptimization(new ParticlesOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new TextureOptimization(priority, 512));
+            result.addOptimization(new TextureOptimization(priority, 512));
 
             // Next priority
             priority++;
-            result.optimizations.push(new RenderTargetsOptimization(priority));
+            result.addOptimization(new RenderTargetsOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new HardwareScalingOptimization(priority, 2));
+            result.addOptimization(new HardwareScalingOptimization(priority, 2));
 
             return result;
         }
 
+        /**
+         * Creates a list of pre-defined optimizations aimed to have a big impact on the scene visual
+         * @param targetFrameRate defines the target frame rate (60 by default)
+         * @returns a SceneOptimizerOptions object
+         */
         public static HighDegradationAllowed(targetFrameRate?: number): SceneOptimizerOptions {
             var result = new SceneOptimizerOptions(targetFrameRate);
 
             var priority = 0;
-            result.optimizations.push(new MergeMeshesOptimization(priority));
-            result.optimizations.push(new ShadowsOptimization(priority));
-            result.optimizations.push(new LensFlaresOptimization(priority));
+            result.addOptimization(new MergeMeshesOptimization(priority));
+            result.addOptimization(new ShadowsOptimization(priority));
+            result.addOptimization(new LensFlaresOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new PostProcessesOptimization(priority));
-            result.optimizations.push(new ParticlesOptimization(priority));
+            result.addOptimization(new PostProcessesOptimization(priority));
+            result.addOptimization(new ParticlesOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new TextureOptimization(priority, 256));
+            result.addOptimization(new TextureOptimization(priority, 256));
 
             // Next priority
             priority++;
-            result.optimizations.push(new RenderTargetsOptimization(priority));
+            result.addOptimization(new RenderTargetsOptimization(priority));
 
             // Next priority
             priority++;
-            result.optimizations.push(new HardwareScalingOptimization(priority, 4));
+            result.addOptimization(new HardwareScalingOptimization(priority, 4));
 
             return result;
         }
     }
 
+    /**
+     * Class used to run optimizations in order to reach a target frame rate
+     * @description More details at http://doc.babylonjs.com/how_to/how_to_use_sceneoptimizer
+     */
+    export class SceneOptimizer implements IDisposable {
+        private _isRunning = false;
+        private _options: SceneOptimizerOptions;
+        private _scene: Scene;
+        private _currentPriorityLevel = 0;
+        private _targetFrameRate = 60;
+        private _trackerDuration = 2000;
+        private _currentFrameRate = 0;
+        private _sceneDisposeObserver: Nullable<Observer<Scene>>;
+
+        /**
+         * Defines an observable called when the optimizer reaches the target frame rate
+         */
+        public onSuccessObservable = new Observable<SceneOptimizer>();
+        /**
+         * Defines an observable called when the optimizer enables an optimization
+         */
+        public onNewOptimizationAppliedObservable = new Observable<SceneOptimization>();
+        /**
+         * Defines an observable called when the optimizer is not able to reach the target frame rate
+         */
+        public onFailureObservable = new Observable<SceneOptimizer>();
+
+        /**
+         * Gets the current priority level (0 at start)
+         */
+        public get currentPriorityLevel(): number {
+            return this._currentPriorityLevel;
+        }
+
+        /**
+         * Gets the current frame rate checked by the SceneOptimizer
+         */
+        public get currentFrameRate(): number {
+            return this._currentFrameRate;
+        }
 
-    // Scene optimizer tool
-    export class SceneOptimizer {
+        /**
+         * Gets or sets the current target frame rate (60 by default)
+         */
+        public get targetFrameRate(): number {
+            return this._targetFrameRate;
+        }
 
-        static _CheckCurrentState(scene: Scene, options: SceneOptimizerOptions, currentPriorityLevel: number, onSuccess?: () => void, onFailure?: () => void) {
-            // TODO: add an epsilon
-            if (scene.getEngine().getFps() >= options.targetFrameRate) {
-                if (onSuccess) {
-                    onSuccess();
+        /**
+         * Gets or sets the current target frame rate (60 by default)
+         */
+        public set targetFrameRate(value: number) {
+            this._targetFrameRate = value;
+        }
+
+        /**
+         * Gets or sets the current interval between two checks (every 2000ms by default)
+         */
+        public get trackerDuration(): number {
+            return this._trackerDuration;
+        }
+
+        /**
+         * Gets or sets the current interval between two checks (every 2000ms by default)
+         */
+        public set trackerDuration(value: number) {
+            this._trackerDuration = value;
+        }
+
+        /**
+         * Gets the list of active optimizations
+         */
+        public get optimizations(): SceneOptimization[] {
+            return this._options.optimizations;
+        }
+
+        /**
+         * Creates a new SceneOptimizer
+         * @param scene defines the scene to work on
+         * @param options defines the options to use with the SceneOptimizer
+         * @param autoGeneratePriorities defines if priorities must be generated and not read from SceneOptimization property (true by default)
+         */
+        public constructor(scene: Scene, options?: SceneOptimizerOptions, autoGeneratePriorities = true) {
+            if (!options) {
+                this._options = new SceneOptimizerOptions();
+            } else {
+                this._options = options;
+            }
+
+            if (this._options.targetFrameRate) {
+                this._targetFrameRate = this._options.targetFrameRate;
+            }
+
+            if (this._options.trackerDuration) {
+                this._trackerDuration = this._options.trackerDuration;
+            }
+
+            if (autoGeneratePriorities) {
+                let priority = 0;
+                for (var optim of this._options.optimizations) {
+                    optim.priority = priority++;
                 }
+            }
 
+            this._scene = scene || Engine.LastCreatedScene;
+            this._sceneDisposeObserver = this._scene.onDisposeObservable.add(() => {
+                this._sceneDisposeObserver = null;
+                this.dispose();
+            })
+        }
+
+        /**
+         * Stops the current optimizer
+         */
+        public stop() {
+            this._isRunning = false;
+        }
+
+        public reset() {
+            this._currentPriorityLevel = 0;
+        }
+
+        public start() {
+            if (this._isRunning) {
+                return;
+            }
+
+            this._isRunning = true;
+
+            // Let's wait for the scene to be ready before running our check
+            this._scene.executeWhenReady(() => {
+                setTimeout(() => {
+                    this._checkCurrentState();
+                }, this._trackerDuration);
+            });
+        }
+
+        private _checkCurrentState() {
+            if (!this._isRunning) {
+                return;
+            }
+
+            let scene = this._scene;
+            let options = this._options;
+
+            this._currentFrameRate = Math.round(scene.getEngine().getFps());
+
+            if (this._currentFrameRate >= this._targetFrameRate) {
+                this._isRunning = false;
+                this.onSuccessObservable.notifyObservers(this);
                 return;
             }
 
@@ -290,45 +686,72 @@
             for (var index = 0; index < options.optimizations.length; index++) {
                 var optimization = options.optimizations[index];
 
-                if (optimization.priority === currentPriorityLevel) {
+                if (optimization.priority === this._currentPriorityLevel) {
                     noOptimizationApplied = false;
                     allDone = allDone && optimization.apply(scene);
+                    this.onNewOptimizationAppliedObservable.notifyObservers(optimization);
                 }
             }
 
             // If no optimization was applied, this is a failure :(
             if (noOptimizationApplied) {
-                if (onFailure) {
-                    onFailure();
-                }
+                this._isRunning = false;
+                this.onFailureObservable.notifyObservers(this);
 
                 return;
             }
 
             // If all optimizations were done, move to next level
             if (allDone) {
-                currentPriorityLevel++;
+                this._currentPriorityLevel++;
             }
 
             // Let's the system running for a specific amount of time before checking FPS
             scene.executeWhenReady(() => {
                 setTimeout(() => {
-                    SceneOptimizer._CheckCurrentState(scene, options, currentPriorityLevel, onSuccess, onFailure);
-                }, options.trackerDuration);
+                    this._checkCurrentState();
+                }, this._trackerDuration);
             });
         }
 
-        public static OptimizeAsync(scene: Scene, options?: SceneOptimizerOptions, onSuccess?: () => void, onFailure?: () => void): void {
-            if (!options) {
-                options = SceneOptimizerOptions.ModerateDegradationAllowed();
+        /**
+         * Release all resources
+         */
+        public dispose(): void {
+            this.stop();
+            this.onSuccessObservable.clear();
+            this.onFailureObservable.clear();
+            this.onNewOptimizationAppliedObservable.clear();
+            if (this._sceneDisposeObserver) {
+                this._scene.onDisposeObservable.remove(this._sceneDisposeObserver);
+            }
+        }
+
+        /**
+         * Helper function to create a SceneOptimizer with one single line of code
+         * @param scene defines the scene to work on
+         * @param options defines the options to use with the SceneOptimizer
+         * @param onSuccess defines a callback to call on success
+         * @param onFailure defines a callback to call on failure
+         */
+        public static OptimizeAsync(scene: Scene, options?: SceneOptimizerOptions, onSuccess?: () => void, onFailure?: () => void): SceneOptimizer {
+            let optimizer = new SceneOptimizer(scene, options || SceneOptimizerOptions.ModerateDegradationAllowed(), false);
+
+            if (onSuccess) {
+                optimizer.onSuccessObservable.add(() => {
+                    onSuccess();
+                });
             }
 
-            // Let's the system running for a specific amount of time before checking FPS
-            scene.executeWhenReady(() => {
-                setTimeout(() => {
-                    SceneOptimizer._CheckCurrentState(scene, <SceneOptimizerOptions>options, 0, onSuccess, onFailure);
-                }, (<SceneOptimizerOptions>options).trackerDuration);
-            });
+            if (onFailure) {
+                optimizer.onFailureObservable.add(() => {
+                    onFailure();
+                });
+            }
+
+            optimizer.start();
+
+            return optimizer;
         }
     }
 } 

+ 119 - 0
src/babylon.assetContainer.ts

@@ -0,0 +1,119 @@
+module BABYLON {
+    export class AssetContainer {
+        public scene: Scene;
+
+        // Objects
+        public cameras = new Array<Camera>();
+        public lights = new Array<Light>();
+        public meshes = new Array<AbstractMesh>();
+        public skeletons = new Array<Skeleton>();
+        public particleSystems = new Array<ParticleSystem>();
+        public animations = new Array<Animation>();
+        public multiMaterials = new Array<MultiMaterial>();
+        public materials = new Array<Material>();
+        public morphTargetManagers = new Array<MorphTargetManager>();
+        public geometries = new Array<Geometry>();
+        public transformNodes = new Array<TransformNode>();
+        public lensFlareSystems = new Array<LensFlareSystem>();
+        public shadowGenerators = new Array<ShadowGenerator>();
+        public actionManagers = new Array<ActionManager>();
+        public sounds = new Array<Sound>();
+        
+        constructor(scene:Scene){
+            this.scene = scene;
+        }
+        
+        addAllToScene(){
+            this.cameras.forEach((o)=>{
+                this.scene.addCamera(o);
+            });
+            this.lights.forEach((o)=>{
+                this.scene.addLight(o);
+            });
+            this.meshes.forEach((o)=>{
+                this.scene.addMesh(o);
+            });
+            this.skeletons.forEach((o)=>{
+                this.scene.addSkeleton(o);
+            });
+            this.particleSystems.forEach((o)=>{
+                this.scene.addParticleSystem(o);
+            });
+            this.animations.forEach((o)=>{
+                this.scene.addAnimation(o);
+            });
+            this.multiMaterials.forEach((o)=>{
+                this.scene.addMultiMaterial(o);
+            });
+            this.materials.forEach((o)=>{
+                this.scene.addMaterial(o);
+            });
+            this.morphTargetManagers.forEach((o)=>{
+                this.scene.addMorphTargetManager(o);
+            });
+            this.geometries.forEach((o)=>{
+                this.scene.addGeometry(o);
+            });
+            this.transformNodes.forEach((o)=>{
+                this.scene.addTransformNode(o);
+            });
+            this.lensFlareSystems.forEach((o)=>{
+                this.scene.addLensFlareSystem(o);
+            });
+            this.actionManagers.forEach((o)=>{
+                this.scene.addActionManager(o);
+            });
+            this.sounds.forEach((o)=>{
+                o.play();
+                o.autoplay=true;
+                this.scene.mainSoundTrack.AddSound(o);
+            })
+        }
+        removeAllFromScene(){
+            this.cameras.forEach((o)=>{
+                this.scene.removeCamera(o);
+            });
+            this.lights.forEach((o)=>{
+                this.scene.removeLight(o);
+            });
+            this.meshes.forEach((o)=>{
+                this.scene.removeMesh(o);
+            });
+            this.skeletons.forEach((o)=>{
+                this.scene.removeSkeleton(o);
+            });
+            this.particleSystems.forEach((o)=>{
+                this.scene.removeParticleSystem(o);
+            });
+            this.animations.forEach((o)=>{
+                this.scene.removeAnimation(o);
+            });
+            this.multiMaterials.forEach((o)=>{
+                this.scene.removeMultiMaterial(o);
+            });
+            this.materials.forEach((o)=>{
+                this.scene.removeMaterial(o);
+            });
+            this.morphTargetManagers.forEach((o)=>{
+                this.scene.removeMorphTargetManager(o);
+            });
+            this.geometries.forEach((o)=>{
+                this.scene.removeGeometry(o);
+            });
+            this.transformNodes.forEach((o)=>{
+                this.scene.removeTransformNode(o);
+            });
+            this.lensFlareSystems.forEach((o)=>{
+                this.scene.removeLensFlareSystem(o);
+            });
+            this.actionManagers.forEach((o)=>{
+                this.scene.removeActionManager(o);
+            });
+            this.sounds.forEach((o)=>{
+                o.stop();
+                o.autoplay = false;
+                this.scene.mainSoundTrack.RemoveSound(o);
+            })
+        }
+    }
+}

+ 0 - 426
src/babylon.mixins.ts

@@ -9,7 +9,6 @@ interface Window {
     WebGLRenderingContext: WebGLRenderingContext;
     MSGesture: MSGesture;
     CANNON: any;
-    SIMD: any;
     AudioContext: AudioContext;
     webkitAudioContext: AudioContext;
     PointerEvent: any;
@@ -141,26 +140,6 @@ interface Math {
     imul(a: number, b: number): number;
 }
 
-interface SIMDglobal {
-    SIMD: SIMD;
-    Math: Math;
-    Uint8Array: Uint8ArrayConstructor;
-    Float32Array: Float32ArrayConstructor;
-}
-
-interface SIMD {
-    Float32x4: SIMD.Float32x4Constructor;
-    Int32x4: SIMD.Int32x4Constructor;
-    Int16x8: SIMD.Int16x8Constructor;
-    Int8x16: SIMD.Int8x16Constructor;
-    Uint32x4: SIMD.Uint32x4Constructor;
-    Uint16x8: SIMD.Uint16x8Constructor;
-    Uint8x16: SIMD.Uint8x16Constructor;
-    Bool32x4: SIMD.Bool32x4Constructor;
-    Bool16x8: SIMD.Bool16x8Constructor;
-    Bool8x16: SIMD.Bool8x16Constructor;
-}
-
 interface GamepadPose {
     hasOrientation: boolean;
     hasPosition: boolean;
@@ -172,411 +151,6 @@ interface GamepadPose {
     angularAcceleration?: Float32Array;
 }
 
-declare namespace SIMD {
-    interface Float32x4 {
-        constructor: Float32x4Constructor;
-        valueOf(): Float32x4;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Float32x4Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number): Float32x4;
-        prototype: Float32x4;
-        extractLane(simd: SIMD.Float32x4, lane: number): number;
-        swizzle(a: SIMD.Float32x4, l1: number, l2: number, l3: number, l4: number): SIMD.Float32x4;
-        shuffle(a: SIMD.Float32x4, b: SIMD.Float32x4, l1: number, l2: number, l3: number, l4: number): SIMD.Float32x4;
-        check(a: SIMD.Float32x4): SIMD.Float32x4;
-        splat(n: number): SIMD.Float32x4;
-        replaceLane(simd: SIMD.Float32x4, lane: number, value: number): SIMD.Float32x4;
-        select(selector: SIMD.Bool32x4, a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        equal(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Bool32x4;
-        notEqual(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Bool32x4;
-        lessThan(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Bool32x4;
-        lessThanOrEqual(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Bool32x4;
-        greaterThan(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Bool32x4;
-        greaterThanOrEqual(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Bool32x4;
-        add(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        sub(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        mul(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        div(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        neg(a: SIMD.Float32x4): SIMD.Float32x4;
-        abs(a: SIMD.Float32x4): SIMD.Float32x4;
-        min(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        max(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        minNum(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        maxNum(a: SIMD.Float32x4, b: SIMD.Float32x4): SIMD.Float32x4;
-        reciprocalApproximation(a: SIMD.Float32x4): SIMD.Float32x4;
-        reciprocalSqrtApproximation(a: SIMD.Float32x4): SIMD.Float32x4;
-        sqrt(a: SIMD.Float32x4): SIMD.Float32x4;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Float32x4;
-        load1(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Float32x4;
-        load2(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Float32x4;
-        load3(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Float32x4;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Float32x4): SIMD.Float32x4;
-        store1(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Float32x4): SIMD.Float32x4;
-        store2(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Float32x4): SIMD.Float32x4;
-        store3(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Float32x4): SIMD.Float32x4;
-        fromInt32x4(value: SIMD.Int32x4): SIMD.Float32x4;
-        fromUint32x4(value: SIMD.Uint32x4): SIMD.Float32x4;
-        fromInt32x4Bits(value: SIMD.Int32x4): SIMD.Float32x4;
-        fromInt16x8Bits(value: SIMD.Int16x8): SIMD.Float32x4;
-        fromInt8x16Bits(value: SIMD.Int8x16): SIMD.Float32x4;
-        fromUint32x4Bits(value: SIMD.Uint32x4): SIMD.Float32x4;
-        fromUint16x8Bits(value: SIMD.Uint16x8): SIMD.Float32x4;
-        fromUint8x16Bits(value: SIMD.Uint8x16): SIMD.Float32x4;
-    }
-
-    interface Int32x4 {
-        constructor: Int32x4Constructor;
-        valueOf(): Int32x4;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Int32x4Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number): Int32x4;
-        prototype: Int32x4;
-        extractLane(simd: SIMD.Int32x4, lane: number): number;
-        swizzle(a: SIMD.Int32x4, l1: number, l2: number, l3: number, l4: number): SIMD.Int32x4;
-        shuffle(a: SIMD.Int32x4, b: SIMD.Int32x4, l1: number, l2: number, l3: number, l4: number): SIMD.Int32x4;
-        check(a: SIMD.Int32x4): SIMD.Int32x4;
-        splat(n: number): SIMD.Int32x4;
-        replaceLane(simd: SIMD.Int32x4, lane: number, value: number): SIMD.Int32x4;
-        select(selector: SIMD.Bool32x4, a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        equal(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Bool32x4;
-        notEqual(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Bool32x4;
-        lessThan(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Bool32x4;
-        lessThanOrEqual(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Bool32x4;
-        greaterThan(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Bool32x4;
-        greaterThanOrEqual(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Bool32x4;
-        and(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        or(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        xor(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        not(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        add(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        sub(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        mul(a: SIMD.Int32x4, b: SIMD.Int32x4): SIMD.Int32x4;
-        neg(a: SIMD.Int32x4): SIMD.Int32x4;
-        shiftLeftByScalar(a: SIMD.Int32x4, bits: number): SIMD.Int32x4;
-        shiftRightByScalar(a: SIMD.Int32x4, bits: number): SIMD.Int32x4;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Int32x4;
-        load1(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Int32x4;
-        load2(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Int32x4;
-        load3(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Int32x4;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Int32x4): SIMD.Int32x4;
-        store1(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Int32x4): SIMD.Int32x4;
-        store2(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Int32x4): SIMD.Int32x4;
-        store3(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Int32x4): SIMD.Int32x4;
-        fromFloat32x4(value: SIMD.Float32x4): SIMD.Int32x4;
-        fromUint32x4(value: SIMD.Uint32x4): SIMD.Int32x4;
-        fromFloat32x4Bits(value: SIMD.Float32x4): SIMD.Int32x4;
-        fromInt16x8Bits(value: SIMD.Int16x8): SIMD.Int32x4;
-        fromInt8x16Bits(value: SIMD.Int8x16): SIMD.Int32x4;
-        fromUint32x4Bits(value: SIMD.Uint32x4): SIMD.Int32x4;
-        fromUint16x8Bits(value: SIMD.Uint16x8): SIMD.Int32x4;
-        fromUint8x16Bits(value: SIMD.Uint8x16): SIMD.Int32x4;
-    }
-
-    interface Int16x8 {
-        constructor: Int16x8Constructor;
-        valueOf(): Int16x8;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Int16x8Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number, s4?: number, s5?: number, s6?: number, s7?: number): Int16x8;
-        prototype: Int16x8;
-        extractLane(simd: SIMD.Int16x8, lane: number): number;
-        swizzle(a: SIMD.Int16x8, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number): SIMD.Int16x8;
-        shuffle(a: SIMD.Int16x8, b: SIMD.Int16x8, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number): SIMD.Int16x8;
-        check(a: SIMD.Int16x8): SIMD.Int16x8;
-        splat(n: number): SIMD.Int16x8;
-        replaceLane(simd: SIMD.Int16x8, lane: number, value: number): SIMD.Int16x8;
-        select(selector: SIMD.Bool16x8, a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        equal(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Bool16x8;
-        notEqual(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Bool16x8;
-        lessThan(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Bool16x8;
-        lessThanOrEqual(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Bool16x8;
-        greaterThan(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Bool16x8;
-        greaterThanOrEqual(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Bool16x8;
-        and(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        or(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        xor(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        not(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        add(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        sub(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        mul(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        neg(a: SIMD.Int16x8): SIMD.Int16x8;
-        shiftLeftByScalar(a: SIMD.Int16x8, bits: number): SIMD.Int16x8;
-        shiftRightByScalar(a: SIMD.Int16x8, bits: number): SIMD.Int16x8;
-        addSaturate(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        subSaturate(a: SIMD.Int16x8, b: SIMD.Int16x8): SIMD.Int16x8;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Int16x8;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Int16x8): SIMD.Int16x8;
-        fromUint16x8(value: SIMD.Uint16x8): SIMD.Int16x8;
-        fromFloat32x4Bits(value: SIMD.Float32x4): SIMD.Int16x8;
-        fromInt32x4Bits(value: SIMD.Int32x4): SIMD.Int16x8;
-        fromInt8x16Bits(value: SIMD.Int8x16): SIMD.Int16x8;
-        fromUint32x4Bits(value: SIMD.Uint32x4): SIMD.Int16x8;
-        fromUint16x8Bits(value: SIMD.Uint16x8): SIMD.Int16x8;
-        fromUint8x16Bits(value: SIMD.Uint8x16): SIMD.Int16x8;
-    }
-
-    interface Int8x16 {
-        constructor: Int8x16Constructor;
-        valueOf(): Int8x16;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Int8x16Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number, s4?: number, s5?: number, s6?: number, s7?: number, s8?: number, s9?: number, s10?: number, s11?: number, s12?: number, s13?: number, s14?: number, s15?: number): Int8x16;
-        prototype: Int8x16;
-        extractLane(simd: SIMD.Int8x16, lane: number): number;
-        swizzle(a: SIMD.Int8x16, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number, l9: number, l10: number, l11: number, l12: number, l13: number, l14: number, l15: number, l16: number): SIMD.Int8x16;
-        shuffle(a: SIMD.Int8x16, b: SIMD.Int8x16, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number, l9: number, l10: number, l11: number, l12: number, l13: number, l14: number, l15: number, l16: number): SIMD.Int8x16;
-        check(a: SIMD.Int8x16): SIMD.Int8x16;
-        splat(n: number): SIMD.Int8x16;
-        replaceLane(simd: SIMD.Int8x16, lane: number, value: number): SIMD.Int8x16;
-        select(selector: SIMD.Bool8x16, a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        equal(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Bool8x16;
-        notEqual(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Bool8x16;
-        lessThan(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Bool8x16;
-        lessThanOrEqual(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Bool8x16;
-        greaterThan(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Bool8x16;
-        greaterThanOrEqual(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Bool8x16;
-        and(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        or(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        xor(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        not(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        add(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        sub(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        mul(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        neg(a: SIMD.Int8x16): SIMD.Int8x16;
-        shiftLeftByScalar(a: SIMD.Int8x16, bits: number): SIMD.Int8x16;
-        shiftRightByScalar(a: SIMD.Int8x16, bits: number): SIMD.Int8x16;
-        addSaturate(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        subSaturate(a: SIMD.Int8x16, b: SIMD.Int8x16): SIMD.Int8x16;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Int8x16;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Int8x16): SIMD.Int8x16;
-        fromUint8x16(value: SIMD.Uint8x16): SIMD.Int8x16;
-        fromFloat32x4Bits(value: SIMD.Float32x4): SIMD.Int8x16;
-        fromInt32x4Bits(value: SIMD.Int32x4): SIMD.Int8x16;
-        fromInt16x8Bits(value: SIMD.Int16x8): SIMD.Int8x16;
-        fromUint32x4Bits(value: SIMD.Uint32x4): SIMD.Int8x16;
-        fromUint16x8Bits(value: SIMD.Uint16x8): SIMD.Int8x16;
-        fromUint8x16Bits(value: SIMD.Uint8x16): SIMD.Int8x16;
-    }
-
-    interface Uint32x4 {
-        constructor: Uint32x4Constructor;
-        valueOf(): Uint32x4;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Uint32x4Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number): Uint32x4;
-        prototype: Uint32x4;
-        extractLane(simd: SIMD.Uint32x4, lane: number): number;
-        swizzle(a: SIMD.Uint32x4, l1: number, l2: number, l3: number, l4: number): SIMD.Uint32x4;
-        shuffle(a: SIMD.Uint32x4, b: SIMD.Uint32x4, l1: number, l2: number, l3: number, l4: number): SIMD.Uint32x4;
-        check(a: SIMD.Uint32x4): SIMD.Uint32x4;
-        splat(n: number): SIMD.Uint32x4;
-        replaceLane(simd: SIMD.Uint32x4, lane: number, value: number): SIMD.Uint32x4;
-        select(selector: SIMD.Bool32x4, a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        equal(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Bool32x4;
-        notEqual(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Bool32x4;
-        lessThan(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Bool32x4;
-        lessThanOrEqual(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Bool32x4;
-        greaterThan(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Bool32x4;
-        greaterThanOrEqual(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Bool32x4;
-        and(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        or(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        xor(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        not(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        add(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        sub(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        mul(a: SIMD.Uint32x4, b: SIMD.Uint32x4): SIMD.Uint32x4;
-        shiftLeftByScalar(a: SIMD.Uint32x4, bits: number): SIMD.Uint32x4;
-        shiftRightByScalar(a: SIMD.Uint32x4, bits: number): SIMD.Uint32x4;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Uint32x4;
-        load1(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Uint32x4;
-        load2(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Uint32x4;
-        load3(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Uint32x4;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Uint32x4): SIMD.Uint32x4;
-        store1(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Uint32x4): SIMD.Uint32x4;
-        store2(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Uint32x4): SIMD.Uint32x4;
-        store3(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Uint32x4): SIMD.Uint32x4;
-        fromFloat32x4(value: SIMD.Float32x4): SIMD.Uint32x4;
-        fromInt32x4(value: SIMD.Int32x4): SIMD.Uint32x4;
-        fromFloat32x4Bits(value: SIMD.Float32x4): SIMD.Uint32x4;
-        fromInt32x4Bits(value: SIMD.Int32x4): SIMD.Uint32x4;
-        fromInt16x8Bits(value: SIMD.Int16x8): SIMD.Uint32x4;
-        fromInt8x16Bits(value: SIMD.Int8x16): SIMD.Uint32x4;
-        fromUint16x8Bits(value: SIMD.Uint16x8): SIMD.Uint32x4;
-        fromUint8x16Bits(value: SIMD.Uint8x16): SIMD.Uint32x4;
-    }
-
-    interface Uint16x8 {
-        constructor: Uint16x8Constructor;
-        valueOf(): Uint16x8;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Uint16x8Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number, s4?: number, s5?: number, s6?: number, s7?: number): Uint16x8;
-        prototype: Uint16x8;
-        extractLane(simd: SIMD.Uint16x8, lane: number): number;
-        swizzle(a: SIMD.Uint16x8, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number): SIMD.Uint16x8;
-        shuffle(a: SIMD.Uint16x8, b: SIMD.Uint16x8, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number): SIMD.Uint16x8;
-        check(a: SIMD.Uint16x8): SIMD.Uint16x8;
-        splat(n: number): SIMD.Uint16x8;
-        replaceLane(simd: SIMD.Uint16x8, lane: number, value: number): SIMD.Uint16x8;
-        select(selector: SIMD.Bool16x8, a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        equal(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Bool16x8;
-        notEqual(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Bool16x8;
-        lessThan(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Bool16x8;
-        lessThanOrEqual(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Bool16x8;
-        greaterThan(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Bool16x8;
-        greaterThanOrEqual(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Bool16x8;
-        and(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        or(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        xor(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        not(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        add(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        sub(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        mul(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        shiftLeftByScalar(a: SIMD.Uint16x8, bits: number): SIMD.Uint16x8;
-        shiftRightByScalar(a: SIMD.Uint16x8, bits: number): SIMD.Uint16x8;
-        addSaturate(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        subSaturate(a: SIMD.Uint16x8, b: SIMD.Uint16x8): SIMD.Uint16x8;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Uint16x8;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Uint16x8): SIMD.Uint16x8;
-        fromInt16x8(value: SIMD.Int16x8): SIMD.Uint16x8;
-        fromFloat32x4Bits(value: SIMD.Float32x4): SIMD.Uint16x8;
-        fromInt32x4Bits(value: SIMD.Int32x4): SIMD.Uint16x8;
-        fromInt16x8Bits(value: SIMD.Int16x8): SIMD.Uint16x8;
-        fromInt8x16Bits(value: SIMD.Int8x16): SIMD.Uint16x8;
-        fromUint32x4Bits(value: SIMD.Uint32x4): SIMD.Uint16x8;
-        fromUint8x16Bits(value: SIMD.Uint8x16): SIMD.Uint16x8;
-    }
-
-    interface Uint8x16 {
-        constructor: Uint8x16Constructor;
-        valueOf(): Uint8x16;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Uint8x16Constructor {
-        (s0?: number, s1?: number, s2?: number, s3?: number, s4?: number, s5?: number, s6?: number, s7?: number, s8?: number, s9?: number, s10?: number, s11?: number, s12?: number, s13?: number, s14?: number, s15?: number): Uint8x16;
-        prototype: Uint8x16;
-        extractLane(simd: SIMD.Uint8x16, lane: number): number;
-        swizzle(a: SIMD.Uint8x16, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number, l9: number, l10: number, l11: number, l12: number, l13: number, l14: number, l15: number, l16: number): SIMD.Uint8x16;
-        shuffle(a: SIMD.Uint8x16, b: SIMD.Uint8x16, l1: number, l2: number, l3: number, l4: number, l5: number, l6: number, l7: number, l8: number, l9: number, l10: number, l11: number, l12: number, l13: number, l14: number, l15: number, l16: number): SIMD.Uint8x16;
-        check(a: SIMD.Uint8x16): SIMD.Uint8x16;
-        splat(n: number): SIMD.Uint8x16;
-        replaceLane(simd: SIMD.Uint8x16, lane: number, value: number): SIMD.Uint8x16;
-        select(selector: SIMD.Bool8x16, a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        equal(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Bool8x16;
-        notEqual(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Bool8x16;
-        lessThan(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Bool8x16;
-        lessThanOrEqual(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Bool8x16;
-        greaterThan(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Bool8x16;
-        greaterThanOrEqual(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Bool8x16;
-        and(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        or(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        xor(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        not(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        add(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        sub(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        mul(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        shiftLeftByScalar(a: SIMD.Uint8x16, bits: number): SIMD.Uint8x16;
-        shiftRightByScalar(a: SIMD.Uint8x16, bits: number): SIMD.Uint8x16;
-        addSaturate(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        subSaturate(a: SIMD.Uint8x16, b: SIMD.Uint8x16): SIMD.Uint8x16;
-        load(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number): SIMD.Uint8x16;
-        store(tarray: Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array, index: number, value: SIMD.Uint8x16): SIMD.Uint8x16;
-        fromInt8x16(value: SIMD.Int8x16): SIMD.Uint8x16;
-        fromFloat32x4Bits(value: SIMD.Float32x4): SIMD.Uint8x16;
-        fromInt32x4Bits(value: SIMD.Int32x4): SIMD.Uint8x16;
-        fromInt16x8Bits(value: SIMD.Int16x8): SIMD.Uint8x16;
-        fromInt8x16Bits(value: SIMD.Int8x16): SIMD.Uint8x16;
-        fromUint32x4Bits(value: SIMD.Uint32x4): SIMD.Uint8x16;
-        fromUint16x8Bits(value: SIMD.Uint16x8): SIMD.Uint8x16;
-    }
-
-    interface Bool32x4 {
-        constructor: Bool32x4Constructor;
-        valueOf(): Bool32x4;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Bool32x4Constructor {
-        (s0?: boolean, s1?: boolean, s2?: boolean, s3?: boolean): Bool32x4;
-        prototype: Bool32x4;
-        extractLane(simd: SIMD.Bool32x4, lane: number): boolean;
-        check(a: SIMD.Bool32x4): SIMD.Bool32x4;
-        splat(n: boolean): SIMD.Bool32x4;
-        replaceLane(simd: SIMD.Bool32x4, lane: number, value: boolean): SIMD.Bool32x4;
-        allTrue(a: SIMD.Bool32x4): boolean;
-        anyTrue(a: SIMD.Bool32x4): boolean;
-        and(a: SIMD.Bool32x4, b: SIMD.Bool32x4): SIMD.Bool32x4;
-        or(a: SIMD.Bool32x4, b: SIMD.Bool32x4): SIMD.Bool32x4;
-        xor(a: SIMD.Bool32x4, b: SIMD.Bool32x4): SIMD.Bool32x4;
-        not(a: SIMD.Bool32x4, b: SIMD.Bool32x4): SIMD.Bool32x4;
-    }
-
-    interface Bool16x8 {
-        constructor: Bool16x8Constructor;
-        valueOf(): Bool16x8;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Bool16x8Constructor {
-        (s0?: boolean, s1?: boolean, s2?: boolean, s3?: boolean, s4?: boolean, s5?: boolean, s6?: boolean, s7?: boolean): Bool16x8;
-        prototype: Bool16x8;
-        extractLane(simd: SIMD.Bool16x8, lane: number): boolean;
-        check(a: SIMD.Bool16x8): SIMD.Bool16x8;
-        splat(n: boolean): SIMD.Bool16x8;
-        replaceLane(simd: SIMD.Bool16x8, lane: number, value: boolean): SIMD.Bool16x8;
-        allTrue(a: SIMD.Bool16x8): boolean;
-        anyTrue(a: SIMD.Bool16x8): boolean;
-        and(a: SIMD.Bool16x8, b: SIMD.Bool16x8): SIMD.Bool16x8;
-        or(a: SIMD.Bool16x8, b: SIMD.Bool16x8): SIMD.Bool16x8;
-        xor(a: SIMD.Bool16x8, b: SIMD.Bool16x8): SIMD.Bool16x8;
-        not(a: SIMD.Bool16x8, b: SIMD.Bool16x8): SIMD.Bool16x8;
-    }
-
-    interface Bool8x16 {
-        constructor: Bool8x16Constructor;
-        valueOf(): Bool8x16;
-        toLocaleString(): string;
-        toString(): string;
-    }
-
-    interface Bool8x16Constructor {
-        (s0?: boolean, s1?: boolean, s2?: boolean, s3?: boolean, s4?: boolean, s5?: boolean, s6?: boolean, s7?: boolean, s8?: boolean, s9?: boolean, s10?: boolean, s11?: boolean, s12?: boolean, s13?: boolean, s14?: boolean, s15?: boolean): Bool8x16;
-        prototype: Bool8x16;
-        extractLane(simd: SIMD.Bool8x16, lane: number): boolean;
-        check(a: SIMD.Bool8x16): SIMD.Bool8x16;
-        splat(n: boolean): SIMD.Bool8x16;
-        replaceLane(simd: SIMD.Bool8x16, lane: number, value: boolean): SIMD.Bool8x16;
-        allTrue(a: SIMD.Bool8x16): boolean;
-        anyTrue(a: SIMD.Bool8x16): boolean;
-        and(a: SIMD.Bool8x16, b: SIMD.Bool8x16): SIMD.Bool8x16;
-        or(a: SIMD.Bool8x16, b: SIMD.Bool8x16): SIMD.Bool8x16;
-        xor(a: SIMD.Bool8x16, b: SIMD.Bool8x16): SIMD.Bool8x16;
-        not(a: SIMD.Bool8x16, b: SIMD.Bool8x16): SIMD.Bool8x16;
-    }
-}
-
 interface EXT_disjoint_timer_query {
     QUERY_COUNTER_BITS_EXT: number;
     TIME_ELAPSED_EXT: number;

+ 99 - 6
src/babylon.scene.ts

@@ -1855,7 +1855,11 @@
             this.onPrePointerObservable.clear();
         }
 
-        // Ready
+        /**
+         * This function will check if the scene can be rendered (textures are loaded, shaders are compiled)
+         * Delay loaded resources are not taking in account
+         * @return true if all required resources are ready
+         */
         public isReady(): boolean {
             if (this._isDisposed) {
                 return false;
@@ -1864,7 +1868,8 @@
             if (this._pendingData.length > 0) {
                 return false;
             }
-            var index: number;
+            let index: number;
+            let engine = this.getEngine();
 
             // Geometries
             for (index = 0; index < this._geometries.length; index++) {
@@ -1891,10 +1896,17 @@
                     return false;
                 }
 
-                var mat = mesh.material;
-                if (mat) {
-                    if (!mat.isReady(mesh)) {
-                        return false;
+                // Highlight layers
+                let hardwareInstancedRendering = mesh.getClassName() === "InstancedMesh" || engine.getCaps().instancedArrays && (<Mesh>mesh).instances.length > 0;
+                for (var layer of this.highlightLayers) {
+                    if (!layer.hasMesh(mesh)) {
+                        continue;
+                    }
+
+                    for (var subMesh of mesh.subMeshes) {
+                        if (!layer.isReady(subMesh, hardwareInstancedRendering)) {
+                            return false;
+                        }
                     }
                 }
             }
@@ -2248,6 +2260,7 @@
             if (this.collisionCoordinator) {
                 this.collisionCoordinator.onMeshAdded(newMesh);
             }
+            newMesh._resyncLightSources();
 
             this.onNewMeshAddedObservable.notifyObservers(newMesh);
         }
@@ -2342,6 +2355,50 @@
             return index;
         }
 
+
+        public removeParticleSystem(toRemove: ParticleSystem): number {
+            var index = this.particleSystems.indexOf(toRemove);
+            if (index !== -1) {
+                this.particleSystems.splice(index, 1);
+            }
+            return index;
+        };
+        public removeAnimation(toRemove: Animation): number {
+            var index = this.animations.indexOf(toRemove);
+            if (index !== -1) {
+                this.animations.splice(index, 1);
+            }
+            return index;
+        };
+        public removeMultiMaterial(toRemove: MultiMaterial): number {
+            var index = this.multiMaterials.indexOf(toRemove);
+            if (index !== -1) {
+                this.multiMaterials.splice(index, 1);
+            }
+            return index;
+        };
+        public removeMaterial(toRemove: Material): number {
+            var index = this.materials.indexOf(toRemove);
+            if (index !== -1) {
+                this.materials.splice(index, 1);
+            }
+            return index;
+        };
+        public removeLensFlareSystem(toRemove: LensFlareSystem) {
+            var index = this.lensFlareSystems.indexOf(toRemove);
+            if (index !== -1) {
+                this.lensFlareSystems.splice(index, 1);
+            }
+            return index;
+        };
+        public removeActionManager(toRemove: ActionManager) {
+            var index = this._actionManagers.indexOf(toRemove);
+            if (index !== -1) {
+                this._actionManagers.splice(index, 1);
+            }
+            return index;
+        };
+
         public addLight(newLight: Light) {
             this.lights.push(newLight);
             this.sortLightsByPriority();
@@ -2368,6 +2425,42 @@
             this.onNewCameraAddedObservable.notifyObservers(newCamera);
         }
 
+        public addSkeleton(newSkeleton: Skeleton) {
+            this.skeletons.push(newSkeleton)
+        }
+
+        public addParticleSystem(newParticleSystem: ParticleSystem) {
+            this.particleSystems.push(newParticleSystem)
+        }
+
+        public addAnimation(newAnimation: Animation) {
+            this.animations.push(newAnimation)
+        }
+
+        public addMultiMaterial(newMultiMaterial: MultiMaterial) {
+            this.multiMaterials.push(newMultiMaterial)
+        }
+
+        public addMaterial(newMaterial: Material) {
+            this.materials.push(newMaterial)
+        }
+
+        public addMorphTargetManager(newMorphTargetManager: MorphTargetManager) {
+            this.morphTargetManagers.push(newMorphTargetManager)
+        }
+
+        public addGeometry(newGeometrie: Geometry) {
+            this._geometries.push(newGeometrie)
+        }
+
+        public addLensFlareSystem(newLensFlareSystem: LensFlareSystem) {
+            this.lensFlareSystems.push(newLensFlareSystem)
+        }
+
+        public addActionManager(newActionManager: ActionManager) {
+            this._actionManagers.push(newActionManager)
+        }
+
         /**
          * Switch active camera
          * @param {Camera} newCamera - new active camera

二进制
tests/validation/ReferenceImages/Billboard.png


二进制
tests/validation/ReferenceImages/DefaultRenderingPipeline.png


二进制
tests/validation/ReferenceImages/Flat2009.png


二进制
tests/validation/ReferenceImages/GUI.png


二进制
tests/validation/ReferenceImages/Hillvalley.png


二进制
tests/validation/ReferenceImages/SpaceDeK.png


二进制
tests/validation/ReferenceImages/TheCar.png


二进制
tests/validation/ReferenceImages/advancedShadows.png


二进制
tests/validation/ReferenceImages/assetContainer.png


二进制
tests/validation/ReferenceImages/charting.png


二进制
tests/validation/ReferenceImages/customRTT.png


二进制
tests/validation/ReferenceImages/instancedBones.png


二进制
tests/validation/ReferenceImages/lod.png


二进制
tests/validation/ReferenceImages/mansion.png


二进制
tests/validation/ReferenceImages/normals.png


二进制
tests/validation/ReferenceImages/pbrrough.png


二进制
tests/validation/ReferenceImages/pointLightShadows.png


二进制
tests/validation/ReferenceImages/procedural.png


二进制
tests/validation/ReferenceImages/refprobe.png


二进制
tests/validation/ReferenceImages/selfShadowing.png


二进制
tests/validation/ReferenceImages/softShadows.png


+ 11 - 30
tests/validation/config.json

@@ -15,9 +15,8 @@
     },
     {
       "title": "Espilit",
-      "renderCount": 10,
       "sceneFolder": "/Scenes/Espilit/",
-      "sceneFilename": "Espilit.binary.babylon",
+      "sceneFilename": "espilit.babylon",
       "referenceImage": "Espilit.png"
     },
     {
@@ -54,7 +53,8 @@
       "title": "Mansion",
       "sceneFolder": "/Scenes/Mansion/",
       "sceneFilename": "Mansion.babylon",
-      "referenceImage": "mansion.png"
+      "referenceImage": "mansion.png",
+      "excludeFromAutomaticTesting": true
     },
     {
       "title": "SpaceDeK",
@@ -64,7 +64,6 @@
     },
     {
       "title": "Flat2009",
-      "renderCount": 20,
       "sceneFolder": "/Scenes/Flat2009/",
       "sceneFilename": "Flat2009.babylon",
       "referenceImage": "Flat2009.png"
@@ -96,7 +95,6 @@
     },
     {
       "title": "Soft Shadows",
-      "renderCount": 5,
       "scriptToRun": "/Demos/SoftShadows/softShadows.js",
       "functionToCall": "CreateSoftShadowsTestScene",
       "referenceImage": "softShadows.png"
@@ -109,7 +107,6 @@
     },
     {
       "title": "Highlights",
-      "renderCount": 10,
       "scriptToRun": "/Demos/Highlights/highlights.js",
       "functionToCall": "CreateHighlightsScene",
       "referenceImage": "highlights.png",
@@ -147,14 +144,12 @@
     },
     {
       "title": "Self shadowing",
-      "renderCount": 10,
       "scriptToRun": "/Demos/SelfShadowing/shadows.js",
       "functionToCall": "CreateShadowsTestScene",
       "referenceImage": "selfShadowing.png"
     },
     {
       "title": "Advanced shadows",
-      "renderCount": 10,
       "scriptToRun": "/Demos/AdvancedShadows/advancedShadows.js",
       "functionToCall": "CreateAdvancedShadowsTestScene",
       "referenceImage": "advancedShadows.png",
@@ -162,7 +157,6 @@
     },
     {
       "title": "point light shadows",
-      "renderCount": 10,
       "scriptToRun": "/Demos/PointLightShadowMap/pointLightShadows.js",
       "functionToCall": "CreatePointLightShadowScene",
       "referenceImage": "pointLightShadows.png",
@@ -182,56 +176,49 @@
     },
     {
       "title": "Custom render target",
+      "renderCount": 20,
       "scriptToRun": "/Demos/CustomRenderTarget/customRenderTarget.js",
-      "renderCount": 10,
       "functionToCall": "CreateCustomRenderTargetTestScene",
       "referenceImage": "customRTT.png"
     },
     {
       "title": "GLTF Normals",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFNormals/index.js",
       "functionToCall": "createScene",
       "referenceImage": "gltfnormals.png"
     },
     {
       "title": "GLTF Material",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFTests/index.js",
       "functionToCall": "createMaterialScene",
       "referenceImage": "gltfMaterial.png"
     },
     {
       "title": "GLTF Material Alpha",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFTests/index.js",
       "functionToCall": "createMaterialAlphaScene",
       "referenceImage": "gltfMaterialAlpha.png"
     },
     {
       "title": "GLTF Primitive Attribute",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFTests/index.js",
       "functionToCall": "createPrimitiveAttributeScene",
       "referenceImage": "gltfPrimitiveAttribute.png"
     },
     {
       "title": "GLTF Metallic Roughness",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFTests/index.js",
       "functionToCall": "createMaterialMetallicRoughnessScene",
       "referenceImage": "gltfMaterialMetallicRoughness.png"
     },
     {
       "title": "GLTF Specular Glossiness",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFTests/index.js",
       "functionToCall": "createMaterialSpecularGlossinessScene",
       "referenceImage": "gltfMaterialSpecularGlossiness.png"
     },
     {
       "title": "GLTF Texture Sampler",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFTests/index.js",
       "functionToCall": "createTextureSamplerScene",
       "referenceImage": "gltfTextureSampler.png"
@@ -245,15 +232,18 @@
       "excludeFromAutomaticTesting": true
     },
     {
+      "title": "Asset Containers",
+      "playgroundId": "#P3U079#19",
+      "referenceImage": "assetContainer.png"
+    },
+    {
       "title": "GLTF Mesh Primitive Attribute Test",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GLTFMeshPrimitiveAttributeTest/index.js",
       "functionToCall": "createScene",
       "referenceImage": "gltfMeshPrimAttribTest.png"
     },
     {
       "title": "PBR glossy",
-      "renderCount": 20,
       "replace": "engine.setHardwareScalingLevel, //engine.setHardwareScalingLevel",
       "scriptToRun": "/Demos/PBRGlossy/index.js",
       "functionToCall": "CreatePBRGlossyScene",
@@ -261,14 +251,13 @@
     },
     {
       "title": "PBR rough",
-      "renderCount": 20,
       "scriptToRun": "/Demos/PBRRough/index.js",
       "functionToCall": "CreatePBRRoughScene",
       "referenceImage": "pbrrough.png"
     },
     {
       "title": "Reflection probes",
-      "renderCount": 10,
+      "renderCount": 20,
       "scriptToRun": "/Demos/RefProbe/reflectionProbe.js",
       "functionToCall": "CreateReflectionProbeTestScene ",
       "referenceImage": "refprobe.png"
@@ -290,44 +279,38 @@
     },
     {
       "title": "MultiSample render targets",
-      "renderCount": 10,
+      "renderCount": 20,
       "playgroundId": "#12MKMN#0",
       "referenceImage": "MultiSample render targets.png"
     },
     {
       "title": "Default rendering pipeline",
-      "renderCount": 20,
       "playgroundId": "#5XB8YT#2",
       "referenceImage": "DefaultRenderingPipeline.png"
     },
     {
       "title": "Billboard",
-      "renderCount": 20,
       "playgroundId": "#UJEIL#13",
       "referenceImage": "Billboard.png"
     },
     {
       "title": "setParent",
-      "renderCount": 20,
       "playgroundId": "#JD49CT#2",
       "referenceImage": "setParent.png"
     },
     {
       "title": "GUI",
-      "renderCount": 20,
       "scriptToRun": "/Demos/GUI/gui.js",
       "functionToCall": "createScene",
       "referenceImage": "GUI.png"
     },
     {
       "title": "Up Vector",
-      "renderCount": 20,
       "playgroundId": "#2FNBTG#27",
       "referenceImage": "upVector.png"
     },
     {
       "title": "Procedural textures",
-      "renderCount": 10,
       "scriptToRun": "/Demos/Procedural/proceduralTexture.js",
       "functionToCall": "CreateProceduralTextureTestScene",
       "referenceImage": "procedural.png",
@@ -336,7 +319,6 @@
     },
     {
       "title": "Instances",
-      "renderCount": 10,
       "scriptToRun": "/Demos/Instances/instances.js",
       "functionToCall": "CreateInstancesTestScene",
       "referenceImage": "instances.png",
@@ -345,7 +327,6 @@
     },
     {
       "title": "Instanced Bones",
-      "renderCount": 10,
       "scriptToRun": "/Demos/InstancedBones/bones2.js",
       "functionToCall": "CreateBones2TestScene",
       "referenceImage": "instancedBones.png",

+ 0 - 0
tests/validation/karma.conf.browserstack.js


部分文件因为文件数量过多而无法显示