Преглед изворни кода

Merge branch 'master' into FollowCamera_PointersInput

dunk пре 6 година
родитељ
комит
fb6a0ef1c1
100 измењених фајлова са 131067 додато и 126638 уклоњено
  1. 6 0
      .travis.yml
  2. 12372 11967
      Playground/babylon.d.txt
  3. 1 1
      Tools/Config/config.json
  4. 2 1
      Tools/Gulp/gulpfile.js
  5. 76 14
      Tools/Gulp/helpers/gulp-validateImports.js
  6. 2 1
      Tools/Gulp/tasks/gulpTasks-importLint.js
  7. 14 4
      Tools/Gulp/tasks/gulpTasks-npmPackages.js
  8. 0 1
      Tools/Gulp/tasks/gulpTasks-tests.js
  9. 83 0
      Tools/Gulp/tasks/gulpTasks-testsES6.js
  10. 1 1
      Tools/Publisher/tasks/buildBabylonJSAndDependencies.js
  11. 1 1
      Tools/Publisher/tasks/main.js
  12. 13437 13026
      dist/preview release/babylon.d.ts
  13. 2 2
      dist/preview release/babylon.js
  14. 5780 5206
      dist/preview release/babylon.max.js
  15. 1 1
      dist/preview release/babylon.max.js.map
  16. 48822 47895
      dist/preview release/babylon.module.d.ts
  17. 1 1
      dist/preview release/glTF2Interface/package.json
  18. 22 11
      dist/preview release/gui/babylon.gui.js
  19. 1 1
      dist/preview release/gui/babylon.gui.js.map
  20. 1 1
      dist/preview release/gui/babylon.gui.min.js
  21. 1 0
      dist/preview release/gui/babylon.gui.module.d.ts
  22. 2 2
      dist/preview release/gui/package.json
  23. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  24. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  25. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  26. 6 6
      dist/preview release/inspector/package.json
  27. 1 0
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  28. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  29. 1 0
      dist/preview release/loaders/babylon.glTFFileLoader.js
  30. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  31. 1 0
      dist/preview release/loaders/babylonjs.loaders.js
  32. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  33. 3 3
      dist/preview release/loaders/package.json
  34. 2 2
      dist/preview release/materialsLibrary/package.json
  35. 1 1
      dist/preview release/package.json
  36. 1 0
      dist/preview release/packagesSizeBaseLine.json
  37. 2 2
      dist/preview release/postProcessesLibrary/package.json
  38. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  39. 1 0
      dist/preview release/serializers/babylon.glTF2Serializer.js
  40. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  41. 1 0
      dist/preview release/serializers/babylonjs.serializers.js
  42. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  43. 3 3
      dist/preview release/serializers/package.json
  44. 48822 47895
      dist/preview release/viewer/babylon.module.d.ts
  45. 0 4
      dist/preview release/viewer/babylon.viewer.d.ts
  46. 184 92
      dist/preview release/viewer/babylon.viewer.js
  47. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  48. 0 5
      dist/preview release/viewer/babylon.viewer.module.d.ts
  49. 10 4
      dist/preview release/what's new.md
  50. 1 0
      gui/src/2D/controls/index.ts
  51. 2 2
      gui/src/3D/controls/button3D.ts
  52. 6 5
      gui/src/3D/controls/holographicButton.ts
  53. 2 2
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  54. 1 1
      inspector/src/components/embedHost/embedHostComponent.tsx
  55. 1 1
      loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts
  56. 2 2
      loaders/src/glTF/2.0/glTFLoader.ts
  57. 1 1
      package.json
  58. 2 1
      readme-es6.md
  59. 2 1
      serializers/src/glTF/2.0/glTFAnimation.ts
  60. 118 0
      src/Actions/abstractActionManager.ts
  61. 33 1
      src/Actions/action.ts
  62. 19 2
      src/Actions/actionEvent.ts
  63. 7 58
      src/Actions/actionManager.ts
  64. 571 0
      src/Animations/animatable.ts
  65. 7 211
      src/Animations/animation.ts
  66. 29 0
      src/Animations/animationEvent.ts
  67. 2 1
      src/Animations/animationGroup.ts
  68. 35 0
      src/Animations/animationKey.ts
  69. 27 0
      src/Animations/animationRange.ts
  70. 5 1
      src/Animations/index.ts
  71. 114 0
      src/Animations/pathCursor.ts
  72. 2 1
      src/Animations/runtimeAnimation.ts
  73. 1 1
      src/Audio/audioEngine.ts
  74. 40 12
      src/Audio/sound.ts
  75. 5 0
      src/Behaviors/Meshes/pointerDragBehavior.ts
  76. 3 73
      src/Bones/bone.ts
  77. 2 1
      src/Bones/skeleton.ts
  78. 2 2
      src/Cameras/Inputs/followCameraMouseWheelInput.ts
  79. 13 1
      src/Cameras/VR/vrExperienceHelper.ts
  80. 5 3
      src/Cameras/arcRotateCamera.ts
  81. 9 19
      src/Cameras/camera.ts
  82. 6 3
      src/Cameras/flyCamera.ts
  83. 5 3
      src/Cameras/freeCamera.ts
  84. 10 23
      src/Collisions/collisionCoordinator.ts
  85. 5 1
      src/Collisions/pickingInfo.ts
  86. 2 1
      src/Culling/Octrees/octreeSceneComponent.ts
  87. 2 2
      src/Culling/boundingInfo.ts
  88. 233 0
      src/Culling/ray.ts
  89. 4 3
      src/Debug/physicsViewer.ts
  90. 2 0
      src/Debug/rayHelper.ts
  91. 3 3
      src/Debug/skeletonViewer.ts
  92. 3 2
      src/Engines/engine.ts
  93. 3 1
      src/Events/pointerEvents.ts
  94. 4 3
      src/Gizmos/axisDragGizmo.ts
  95. 4 3
      src/Gizmos/axisScaleGizmo.ts
  96. 21 16
      src/Gizmos/boundingBoxGizmo.ts
  97. 3 0
      src/Gizmos/planeRotationGizmo.ts
  98. 3 3
      src/Gizmos/scaleGizmo.ts
  99. 4 0
      src/Helpers/environmentHelper.ts
  100. 0 0
      src/Helpers/photoDome.ts

+ 6 - 0
.travis.yml

@@ -10,6 +10,8 @@ cache:
   - node_modules
   - Playground/node_modules
 install:
+- rm node_modules/@babylonjs/core -f
+- rm node_modules/@babylonjs/materials -f
 - travis_retry npm install -g gulp@4.0.0
 - travis_retry npm install -g typescript
 - cd ./Tools/Gulp
@@ -35,7 +37,11 @@ jobs:
     - gulp tests-babylon-unit
     - travis_retry gulp tests-validation-virtualscreen
     - travis_retry gulp tests-validation-browserstack
+  - env: JOB=ModuleTests
+    script:
+    - gulp typescript-all
     - gulp tests-modules
+    - gulp deployAndTests-es6Modules
   - env: JOB=ViewerTests
     script:
     - export DISPLAY=:99.0

Разлика између датотеке није приказан због своје велике величине
+ 12372 - 11967
Playground/babylon.d.txt


+ 1 - 1
Tools/Config/config.json

@@ -60,7 +60,7 @@
     "additionalNpmPackages": [
         {
             "name": "gltf2interface",
-            "path": "dist/preview release/gltf2interface"
+            "path": "dist/preview release/glTF2Interface"
         }
     ],
     "core": {

+ 2 - 1
Tools/Gulp/gulpfile.js

@@ -18,6 +18,7 @@ require("./tasks/gulpTasks-tests");
 require("./tasks/gulpTasks-remapPaths");
 require("./tasks/gulpTasks-npmPackages");
 require("./tasks/gulpTasks-dependencies");
+require("./tasks/gulpTasks-testsES6");
 
 /**
  * Temp cleanup after upgrade.
@@ -79,7 +80,7 @@ gulp.task("typescript-all", gulp.series("typescript-libraries", "typescript-es6"
 /**
  * Do it all (tests).
  */
-gulp.task("tests-all", gulp.series("tests-unit", "tests-modules", "tests-validation-virtualscreen", "tests-validation-browserstack"));
+gulp.task("tests-all", gulp.series("tests-unit", "tests-modules", "deployAndTests-es6Modules", "tests-validation-virtualscreen", "tests-validation-browserstack"));
 
 /**
  * Get Ready to test Npm Packages.

+ 76 - 14
Tools/Gulp/helpers/gulp-validateImports.js

@@ -8,43 +8,63 @@ var colorConsole = require("../../NodeHelpers/colorConsole");
 var config = require("../../Config/config");
 
 const indexExlclusion = ["States", "EmitterTypes"];
+const forbiddenImports = ["meshBuilder"];
 
 const mapping = { };
 config.modules.forEach(moduleName => {
     mapping[config[moduleName].build.umd.packageName] = moduleName;
 });
 
-var validatePath = function(fileLocation, directory, module, lineNumber, errors) {
+var validatePath = function(fileLocation, directory, module, lineNumber, errors, isExport) {
+    let expressionType = isExport ? "Export" : "Import";
     let internalModulePath = path.join(directory, module + ".ts");
+
     // Check .ts path.
     if (!fs.existsSync(internalModulePath)) {
-        let internalModulePath = path.join(directory, module + ".tsx");
+        internalModulePath = path.join(directory, module + ".tsx");
         // Check .tsx path.
         if (!fs.existsSync(internalModulePath)) {
             // If not found, check index.ts for legacy and index files.
             if (fileLocation.indexOf("legacy") > -1 || fileLocation.indexOf("index") > -1) {
-                let internalModulePath = path.join(directory, module, "index.ts");
+                internalModulePath = path.join(directory, module, "index.ts");
                 if (!fs.existsSync(internalModulePath)) {
-                    errors.push(`Line ${lineNumber} Export from folder only allowes if index is present. ${module}`);
+                    errors.push(`Line ${lineNumber} ${expressionType} from folder only allows if index is present. ${module}`);
                 }
             }
             else {
-                errors.push(`Line ${lineNumber} Imports ${module} needs to be full path (not from directory) for tree shaking.`);
+                errors.push(`Line ${lineNumber} ${expressionType}s ${module} needs to be full path (not from directory) for tree shaking.`);
             }
         }
     }
 
     if (internalModulePath.indexOf("index.") > -1) {
         if (fileLocation.indexOf("legacy") === -1) {
-            let excluded = false;
-            for (let exclusion of indexExlclusion) {
-                if (internalModulePath.indexOf(exclusion) > -1) {
-                    excluded = true;
-                    break;
+            if (isExport) {
+                internalModulePath = path.join(directory, module + ".ts");
+                // Check .ts path.
+                if (!fs.existsSync(internalModulePath)) {
+                    errors.push(`Line ${lineNumber} Exports ${module} should be from the full path including index.`);
+                }
+            }
+            else {
+                let excluded = false;
+                for (let exclusion of indexExlclusion) {
+                    if (internalModulePath.indexOf(exclusion) > -1) {
+                        excluded = true;
+                        break;
+                    }
+                }
+                if (!excluded && fileLocation.indexOf("index.ts") === -1) {
+                    errors.push(`Line ${lineNumber} Imports ${module} should not be from index for tree shaking.`);
                 }
             }
-            if (!excluded) {
-                errors.push(`Line ${lineNumber} Imports ${module} should not be from index for tree shaking.`);
+        }
+    }
+
+    if (!isExport) {
+        for (let forbiddenImport of forbiddenImports) {
+            if (module.endsWith(forbiddenImport)) {
+                errors.push(`Line ${lineNumber} ${expressionType}s ${module} is forbidden for tree shaking.`);
             }
         }
     }
@@ -100,7 +120,7 @@ var validateImports = function(data, fileLocation, options) {
 
                     const directory = config[configName].computed.srcDirectory;
                     module = module.substring(splitter);
-                    validatePath(fileLocation, directory, module, index + 1, errors);
+                    validatePath(fileLocation, directory, module, index + 1, errors, false);
                 }
             }
             else {
@@ -110,7 +130,49 @@ var validateImports = function(data, fileLocation, options) {
                 }
                 else {
                     const directory = path.dirname(fileLocation);
-                    validatePath(fileLocation, directory, module, index + 1, errors);
+                    validatePath(fileLocation, directory, module, index + 1, errors, false);
+                }
+            }
+        }
+
+        // Find Exports.
+        if (options.isCore && line.indexOf("export") > -1) {
+            let regexTypeExport = new RegExp(`export .* from ['"](.*)['"];`, "g");
+            let match = regexTypeExport.exec(line);
+            if (match) {
+                module = match[1];
+
+                // Checks if line is about external module
+                if (options.externals) {
+                    for (let ext in options.externals) {
+                        if (line.indexOf(ext) > -1) {
+                            externalModule = ext;
+                            break;
+                        }
+                    }
+                }
+
+                // Check if path is correct internal.
+                if (externalModule) {
+                    const splitter = module.indexOf("/");
+                    const baseModule = module.substring(0, splitter);
+                    if (mapping[baseModule]) {
+                        const configName = mapping[baseModule];
+
+                        const directory = config[configName].computed.srcDirectory;
+                        module = module.substring(splitter);
+                        validatePath(fileLocation, directory, module, index + 1, errors, true);
+                    }
+                }
+                else {
+                    // Check Relative.
+                    if (!module.startsWith(".")) {
+                        errors.push(`Line ${index + 1} Export ${module} needs to be relative.`);
+                    }
+                    else {
+                        const directory = path.dirname(fileLocation);
+                        validatePath(fileLocation, directory, module, index + 1, errors, true);
+                    }
                 }
             }
         }

+ 2 - 1
Tools/Gulp/tasks/gulpTasks-importLint.js

@@ -27,7 +27,8 @@ var importLintLibrary = function(settings) {
     return gulp.src(settings.computed.tsGlob)
         .pipe(fxFilter)
         .pipe(validateImports({
-            externals: settings.build.umd.processDeclaration.classMap
+            externals: settings.build.umd.processDeclaration.classMap,
+            isCore: settings.isCore
         }));
 }
 

+ 14 - 4
Tools/Gulp/tasks/gulpTasks-npmPackages.js

@@ -7,22 +7,32 @@ var publish = require("../../Publisher/tasks/main");
 /**
  * Get Ready to test Npm Packages.
  */
-gulp.task("npmPackages-es6", gulp.series("typescript-es6", function(cb) {
+gulp.task("localdev-es6", function(cb) {
     publish(false, {
         es6: true
     });
     cb();
-}));
+});
 
 /**
  * Get Ready to test Npm Packages.
  */
-gulp.task("npmPackages-UMD", gulp.series("typescript-libraries", function(cb) {
+gulp.task("localdev-UMD", function(cb) {
     publish(false, {
         umd: true
     });
     cb();
-}));
+});
+
+/**
+ * Get Ready to test Npm Packages.
+ */
+gulp.task("npmPackages-es6", gulp.series("typescript-es6", "localdev-es6"));
+
+/**
+ * Get Ready to test Npm Packages.
+ */
+gulp.task("npmPackages-UMD", gulp.series("typescript-libraries", "localdev-UMD"));
 
 /**
  * Get Ready to test Npm Packages.

+ 0 - 1
Tools/Gulp/tasks/gulpTasks-tests.js

@@ -1,7 +1,6 @@
 // Import Dependencies.
 var gulp = require("gulp");
 var typescript = require("gulp-typescript");
-var fs = require("fs");
 var karmaServer = require('karma').Server;
 var webpack = require('webpack');
 var webpackStream = require("webpack-stream");

+ 83 - 0
Tools/Gulp/tasks/gulpTasks-testsES6.js

@@ -0,0 +1,83 @@
+// Import Dependencies.
+const gulp = require("gulp");
+const path = require("path");
+const fs = require("fs-extra");
+const shelljs = require('shelljs');
+
+// Import Helpers.
+const colorConsole = require("../../NodeHelpers/colorConsole");
+
+// Read the full config.
+var config = require("../../Config/config.js");
+
+// Base Line Path.
+var baseLinePath = path.resolve(config.computed.rootFolder, "dist/preview release/packagesSizeBaseLine.json");
+var es6TestsFolder = path.resolve(config.computed.rootFolder, "tests/es6Modules");
+var es6TestsWebpackFile = path.resolve(es6TestsFolder, "webpack.config.js");
+
+/**
+ * Launches the ES6 modules tests to evaluate the min package size.
+ */
+gulp.task("tests-es6Modules", function(done) {
+    colorConsole.log("Npm link dependencies");
+    shelljs.exec("npm link @babylonjs/core", {
+        async: false,
+        cwd: es6TestsFolder
+    });
+
+    shelljs.exec("npm link @babylonjs/materials", {
+        async: false,
+        cwd: es6TestsFolder
+    });
+
+    colorConsole.log("Bundle test app");
+    var result = shelljs.exec("npx webpack", {
+        async: false,
+        cwd: es6TestsFolder
+    });
+
+    if (result.code != 0) {
+        colorConsole.error(result.stdout);
+        colorConsole.error(result.stderr);
+        throw "Can not build es6 dev apps."
+    }
+
+    colorConsole.log("Gather output size");
+
+    var testsBaseLine = fs.readJSONSync(baseLinePath);
+    var webpackConfig = require(es6TestsWebpackFile);
+    for (let entryName in webpackConfig.entry) {
+        let entry = webpackConfig.entry[entryName];
+        entry = entry.replace(".ts", ".js");
+        entry = path.basename(entry);
+        let outputPath = path.resolve(config.computed.tempFolder, 'testsES6Modules', entry);
+        let stats = fs.statSync(outputPath);
+        let size = stats.size;
+
+        if (testsBaseLine && testsBaseLine[entryName] && size > (testsBaseLine[entryName] + 10000)) {
+            colorConsole.error(`New size: ${(""+size).cyan} bytes is bigger than baseline size : ${testsBaseLine[entryName]} bytes on ${entryName}.`);
+            throw "Bigger than baseline";
+        }
+        testsBaseLine[entryName] = size;
+    }
+
+    for (let entryName in testsBaseLine) {
+        colorConsole.success(`Baseline size for ${entryName.yellow} is ${(""+testsBaseLine[entryName]).cyan} bytes.`);
+    }
+
+    colorConsole.log("Save baseline");
+    fs.writeJSONSync(baseLinePath, testsBaseLine);
+
+    done();
+});
+
+/**
+ * Launches the ES6 modules tests to evaluate the min package size.
+ */
+gulp.task("deployAndTests-es6Modules", gulp.series("localdev-es6", "tests-es6Modules"));
+
+
+/**
+ * Launches the ES6 modules tests to evaluate the min package size.
+ */
+gulp.task("buildAndTests-es6Modules", gulp.series("npmPackages-es6", "deployAndTests-es6Modules"));

+ 1 - 1
Tools/Publisher/tasks/buildBabylonJSAndDependencies.js

@@ -8,7 +8,7 @@ const colorConsole = require("../../NodeHelpers/colorConsole");
  */
 function buildBabylonJSAndDependencies() {
     colorConsole.log("Running gulp compilation");
-    let exec = shelljs.exec("gulp typescript-libraries typescript-es6", {
+    let exec = shelljs.exec("gulp typescript-all", {
         cwd: path.resolve(__dirname, "../../Gulp/")
     });
     if (exec.code) {

+ 1 - 1
Tools/Publisher/tasks/main.js

@@ -58,8 +58,8 @@ module.exports = function(production, options) {
                 return;
             }
 
-            buildBabylonJSAndDependencies();
             versionNumberManager.updateEngineVersion(version);
+            buildBabylonJSAndDependencies();
             versionNumberManager.updateRootPackageVersion(version);
 
             process.env.BABYLONJSREALPUBLISH = true;

Разлика између датотеке није приказан због своје велике величине
+ 13437 - 13026
dist/preview release/babylon.d.ts


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
dist/preview release/babylon.js


Разлика између датотеке није приказан због своје велике величине
+ 5780 - 5206
dist/preview release/babylon.max.js


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/babylon.max.js.map


Разлика између датотеке није приказан због своје велике величине
+ 48822 - 47895
dist/preview release/babylon.module.d.ts


+ 1 - 1
dist/preview release/glTF2Interface/package.json

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

Разлика између датотеке није приказан због своје велике величине
+ 22 - 11
dist/preview release/gui/babylon.gui.js


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


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

@@ -2994,6 +2994,7 @@ declare module "babylonjs-gui/2D/controls/index" {
     export * from "babylonjs-gui/2D/controls/sliders/baseSlider";
     export * from "babylonjs-gui/2D/controls/sliders/slider";
     export * from "babylonjs-gui/2D/controls/sliders/imageBasedSlider";
+    export * from "babylonjs-gui/2D/controls/sliders/scrollBar";
     export * from "babylonjs-gui/2D/controls/statics";
 }
 declare module "babylonjs-gui/2D/adtInstrumentation" {

+ 2 - 2
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.19"
+        "babylonjs": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.js


+ 2 - 2
dist/preview release/inspector/babylon.inspector.bundle.max.js

@@ -37960,7 +37960,7 @@ var MeshPropertyGridComponent = /** @class */ (function (_super) {
             var v2 = v1.add(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__["Vector3"].FromArray(normals, i).scaleInPlace(size));
             lines.push([v1, v2]);
         }
-        var normalLines = babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__["MeshBuilder"].CreateLineSystem("normalLines", { lines: lines }, scene);
+        var normalLines = babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_2__["LinesBuilder"].CreateLineSystem("normalLines", { lines: lines }, scene);
         normalLines.color = color;
         normalLines.parent = mesh;
         if (!mesh.reservedDataStore) {
@@ -38918,7 +38918,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
-var Split = __webpack_require__(/*! split.js */ "../node_modules/split.js/dist/split.js");
+var Split = __webpack_require__(/*! split.js */ "../node_modules/split.js/dist/split.js").default;
 __webpack_require__(/*! ./embedHost.scss */ "./components/embedHost/embedHost.scss");
 var EmbedHostComponent = /** @class */ (function (_super) {
     tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"](EmbedHostComponent, _super);

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 6 - 6
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -31,11 +31,11 @@
     "dependencies": {
         "@types/react": "~16.7.3",
         "@types/react-dom": "~16.0.9",
-        "babylonjs": "4.0.0-alpha.19",
-        "babylonjs-gui": "4.0.0-alpha.19",
-        "babylonjs-loaders": "4.0.0-alpha.19",
-        "babylonjs-serializers": "4.0.0-alpha.19",
-        "babylonjs-gltf2interface": "4.0.0-alpha.19"
+        "babylonjs": "4.0.0-alpha.21",
+        "babylonjs-gui": "4.0.0-alpha.21",
+        "babylonjs-loaders": "4.0.0-alpha.21",
+        "babylonjs-serializers": "4.0.0-alpha.21",
+        "babylonjs-gltf2interface": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

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

@@ -1363,6 +1363,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * Helper class for working with arrays when loading the glTF asset
  */

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js.map


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

@@ -3912,6 +3912,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * Helper class for working with arrays when loading the glTF asset
  */

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js.map


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

@@ -5166,6 +5166,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * Helper class for working with arrays when loading the glTF asset
  */

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.js.map


+ 3 - 3
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.19",
-        "babylonjs": "4.0.0-alpha.19"
+        "babylonjs-gltf2interface": "4.0.0-alpha.21",
+        "babylonjs": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.19"
+        "babylonjs": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

+ 1 - 1
dist/preview release/package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 0
dist/preview release/packagesSizeBaseLine.json

@@ -0,0 +1 @@
+{"engineOnly":303853,"sceneOnly":503858,"minGridMaterial":619343,"minStandardMaterial":740051}

+ 2 - 2
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.19"
+        "babylonjs": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.19"
+        "babylonjs": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

+ 1 - 0
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -266,6 +266,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * @hidden
  * Enum for handling in tangent and out tangent.

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js.map


+ 1 - 0
dist/preview release/serializers/babylonjs.serializers.js

@@ -444,6 +444,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * @hidden
  * Enum for handling in tangent and out tangent.

Разлика између датотеке није приказан због своје велике величине
+ 1 - 1
dist/preview release/serializers/babylonjs.serializers.js.map


+ 3 - 3
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.19",
-        "babylonjs-gltf2interface": "4.0.0-alpha.19"
+        "babylonjs": "4.0.0-alpha.21",
+        "babylonjs-gltf2interface": "4.0.0-alpha.21"
     },
     "engines": {
         "node": "*"

Разлика између датотеке није приказан због своје велике величине
+ 48822 - 47895
dist/preview release/viewer/babylon.module.d.ts


+ 0 - 4
dist/preview release/viewer/babylon.viewer.d.ts

@@ -2,10 +2,6 @@
 /// <reference path="./babylon.glTF2Interface.d.ts"/>
 /// <reference path="./babylonjs.loaders.d.ts"/>
 declare module "babylonjs-loaders"{ export=BABYLON;}
-/// <reference path="./babylon.module.d.ts"/>
-/// <reference path="./babylon.glTF2Interface.d.ts"/>
-/// <reference path="./babylonjs.loaders.d.ts"/>
-declare module "babylonjs-loaders"{ export=BABYLON;}
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 //   ../../../../../Tools/Gulp/babylonjs

Разлика између датотеке није приказан због своје велике величине
+ 184 - 92
dist/preview release/viewer/babylon.viewer.js


Разлика између датотеке није приказан због своје велике величине
+ 2 - 2
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -3,11 +3,6 @@
 /// <reference path="./babylonjs.loaders.d.ts"/>
 declare module "babylonjs-loaders"{ export=BABYLON;}
 
-/// <reference path="./babylon.module.d.ts"/>
-/// <reference path="./babylon.glTF2Interface.d.ts"/>
-/// <reference path="./babylonjs.loaders.d.ts"/>
-declare module "babylonjs-loaders"{ export=BABYLON;}
-
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 //   ../../../../../Tools/Gulp/babylonjs

+ 10 - 4
dist/preview release/what's new.md

@@ -91,11 +91,14 @@
 - Fix typo in FollowCamera InputsManager when limiting rotation to 360 degrees. ([mrdunk](https://github.com))
 - In FollowCamera InputsManager, allow choice of modifier key (Alt, Ctrl and/or Shift) for each camera movement axis. ([mrdunk](https://github.com))
 - Added MouseWheel bindings for FollowCamera. ([mrdunk](https://github.com))
+- Tweak MouseWheel bindings for FollowCamera orientations. ([mrdunk](https://github.com))
 - Added maximum and minimum limits for FollowCamera parameters. ([mrdunk](https://github.com))
 - Added per solid particle culling possibility : `solidParticle.isInFrustum()`  ([jerome](https://github.com/jbousquie))
 - Added transparency support to `GlowLayer` ([Sebavan](https://github.com/Sebavan))
 - Added option `forceDisposeChildren` to multiMaterial.dispose ([danjpar](https://github.com/danjpar))
 - Added Pointer bindings for FollowCamera. ([mrdunk](https://github.com))
+- Added option `multiMultiMaterials` to mesh.mergeMeshes ([danjpar](https://github.com/danjpar))
+- Expose fallback camera distortion metrics option in vrExperienceHelper ([TrevorDev](https://github.com/TrevorDev))
 
 ### OBJ Loader
 - Add color vertex support (not part of standard) ([brianzinn](https://github.com/brianzinn))
@@ -151,8 +154,9 @@
 - Fix more case sensitive paths ([mrdunk](https://github.com))
 - Attaching a BoundingBoxGizmo on a child should not remove its parent ([TrevorDev](https://github.com/TrevorDev)))
 - AmmoJS fix include issue caused after modules update and use world contact point to be consistent with oimo and cannon ([TrevorDev](https://github.com/TrevorDev)))
-- Oimo disable motor maxForce, cannonJS support no impostor, cannonJS cylinder axis, ammoJS wake up impostor when apply force/impulse ([TrevorDev](https://github.com/TrevorDev)))
+- Warn of motor with maxForce in Oimo, cannonJS support no impostor, cannonJS cylinder axis, ammoJS wake up impostor when apply force/impulse ([TrevorDev](https://github.com/TrevorDev)))
 - Utility layer should render on last active camera ([TrevorDev](https://github.com/TrevorDev))
+- PointerDragBehavior should not let the drag plane get out of sync when rotating the object during dragging ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
@@ -217,7 +221,9 @@
 - `Viewport.toglobal` does not allow passing engine in to prevent circular dependency ([Sebavan](https://github.com/Sebavan))
 - `Vector3.UnprojectRayToRef` has been moved to `Ray.unprojectRayToRef` instance method to decrease class coupling ([Sebavan](https://github.com/Sebavan))
 - `Material.ParseMultiMaterial` has been moved to `MultiMaterial.ParseMultiMaterial` to decrease class coupling ([Sebavan](https://github.com/Sebavan))
-- no more `babylon.no-module.max.js` javascript version has the Webpack UMD bundle covers both ([Sebavan]
+- No more `babylon.no-module.max.js` javascript version has the Webpack UMD bundle covers both ([Sebavan]
 (https://github.com/Sebavan))
-- no more `es6.js` javascript as it is now available as a true es6 npm package ([Sebavan](https://github.com/Sebavan))
-- no more `babylon.worker.js` javascript following the lack of usage from the feature ([Sebavan](https://github.com/Sebavan))
+- No more `es6.js` javascript as it is now available as a true es6 npm package ([Sebavan](https://github.com/Sebavan))
+- No more `babylon.worker.js` javascript following the lack of usage from the feature ([Sebavan]
+(https://github.com/Sebavan))
+- No more `Primitive Geometries` as they were not in use since 2.0 ([Sebavan](https://github.com/Sebavan))

+ 1 - 0
gui/src/2D/controls/index.ts

@@ -21,5 +21,6 @@ export * from "./displayGrid";
 export * from "./sliders/baseSlider";
 export * from "./sliders/slider";
 export * from "./sliders/imageBasedSlider";
+export * from "./sliders/scrollBar";
 
 export * from "./statics";

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

@@ -2,7 +2,7 @@ import { int, Nullable } from "babylonjs/types";
 import { Color3, Vector4 } from "babylonjs/Maths/math";
 import { TransformNode } from "babylonjs/Meshes/transformNode";
 import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
-import { MeshBuilder } from "babylonjs/Meshes/meshBuilder";
+import { BoxBuilder } from "babylonjs/Meshes/Builders/boxBuilder";
 import { Material } from "babylonjs/Materials/material";
 import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
 import { Texture } from "babylonjs/Materials/Textures/texture";
@@ -152,7 +152,7 @@ export class Button3D extends AbstractButton3D {
         }
         faceUV[1] = new Vector4(0, 0, 1, 1);
 
-        let mesh = MeshBuilder.CreateBox(this.name + "_rootMesh", {
+        let mesh = BoxBuilder.CreateBox(this.name + "_rootMesh", {
             width: 1.0,
             height: 1.0,
             depth: 0.08,

+ 6 - 5
gui/src/3D/controls/holographicButton.ts

@@ -6,7 +6,8 @@ import { Color3, Vector3 } from "babylonjs/Maths/math";
 import { StandardMaterial } from "babylonjs/Materials/standardMaterial";
 import { TransformNode } from "babylonjs/Meshes/transformNode";
 import { Mesh } from "babylonjs/Meshes/mesh";
-import { MeshBuilder } from "babylonjs/Meshes/meshBuilder";
+import { PlaneBuilder } from "babylonjs/Meshes/Builders/planeBuilder";
+import { BoxBuilder } from "babylonjs/Meshes/Builders/boxBuilder";
 import { FadeInOutBehavior } from "babylonjs/Behaviors/Meshes/fadeInOutBehavior";
 import { Scene } from "babylonjs/scene";
 
@@ -65,8 +66,8 @@ export class HolographicButton extends Button3D {
         }
         if (!this._tooltipFade) {
             // Create tooltip with mesh and text
-            this._tooltipMesh = MeshBuilder.CreatePlane("", { size: 1 }, this._backPlate._scene);
-            var tooltipBackground = MeshBuilder.CreatePlane("", { size: 1, sideOrientation: Mesh.DOUBLESIDE }, this._backPlate._scene);
+            this._tooltipMesh = PlaneBuilder.CreatePlane("", { size: 1 }, this._backPlate._scene);
+            var tooltipBackground = PlaneBuilder.CreatePlane("", { size: 1, sideOrientation: Mesh.DOUBLESIDE }, this._backPlate._scene);
             var mat = new StandardMaterial("", this._backPlate._scene);
             mat.diffuseColor = Color3.FromHexString("#212121");
             tooltipBackground.material = mat;
@@ -235,13 +236,13 @@ export class HolographicButton extends Button3D {
 
     // Mesh association
     protected _createNode(scene: Scene): TransformNode {
-        this._backPlate = MeshBuilder.CreateBox(this.name + "BackMesh", {
+        this._backPlate = BoxBuilder.CreateBox(this.name + "BackMesh", {
             width: 1.0,
             height: 1.0,
             depth: 0.08
         }, scene);
 
-        this._frontPlate = MeshBuilder.CreateBox(this.name + "FrontMesh", {
+        this._frontPlate = BoxBuilder.CreateBox(this.name + "FrontMesh", {
             width: 1.0,
             height: 1.0,
             depth: 0.08

+ 2 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx

@@ -5,7 +5,7 @@ import { Tools } from "babylonjs/Misc/tools";
 import { Color3, Vector3 } from "babylonjs/Maths/math";
 import { Mesh } from "babylonjs/Meshes/mesh";
 import { VertexBuffer } from "babylonjs/Meshes/buffer";
-import { MeshBuilder } from "babylonjs/Meshes/meshBuilder";
+import { LinesBuilder } from "babylonjs/Meshes/Builders/linesBuilder";
 import { PhysicsImpostor } from "babylonjs/Physics/physicsImpostor";
 import { Scene } from "babylonjs/scene";
 
@@ -60,7 +60,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
             lines.push([v1, v2]);
         }
 
-        var normalLines = MeshBuilder.CreateLineSystem("normalLines", { lines: lines }, scene);
+        var normalLines = LinesBuilder.CreateLineSystem("normalLines", { lines: lines }, scene);
         normalLines.color = color;
         normalLines.parent = mesh;
 

+ 1 - 1
inspector/src/components/embedHost/embedHostComponent.tsx

@@ -6,7 +6,7 @@ import { ActionTabsComponent } from "../actionTabs/actionTabsComponent";
 import { Scene } from "babylonjs/scene";
 import { GlobalState } from "components/globalState";
 
-const Split = require('split.js')
+const Split = require('split.js').default;
 
 require("./embedHost.scss");
 

+ 1 - 1
loaders/src/glTF/2.0/Extensions/MSFT_audio_emitter.ts

@@ -2,7 +2,7 @@ import { Nullable } from "babylonjs/types";
 import { Vector3 } from "babylonjs/Maths/math";
 import { Tools } from "babylonjs/Misc/tools";
 import { AnimationGroup } from "babylonjs/Animations/animationGroup";
-import { AnimationEvent } from "babylonjs/Animations/animation";
+import { AnimationEvent } from "babylonjs/Animations/animationEvent";
 import { TransformNode } from "babylonjs/Meshes/transformNode";
 import { Sound } from "babylonjs/Audio/sound";
 import { WeightedSound } from "babylonjs/Audio/weightedsound";

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

@@ -5,7 +5,7 @@ import { LoadFileError, IFileRequest, Tools } from "babylonjs/Misc/tools";
 import { Camera } from "babylonjs/Cameras/camera";
 import { FreeCamera } from "babylonjs/Cameras/freeCamera";
 import { AnimationGroup } from "babylonjs/Animations/animationGroup";
-import { Animation, IAnimationKey, AnimationKeyInterpolation } from "babylonjs/Animations/animation";
+import { Animation } from "babylonjs/Animations/animation";
 import { Bone } from "babylonjs/Bones/bone";
 import { Skeleton } from "babylonjs/Bones/skeleton";
 import { IParticleSystem } from "babylonjs/Particles/IParticleSystem";
@@ -22,11 +22,11 @@ import { MorphTarget } from "babylonjs/Morph/morphTarget";
 import { MorphTargetManager } from "babylonjs/Morph/morphTargetManager";
 import { SceneLoaderProgressEvent } from "babylonjs/Loading/sceneLoader";
 import { Scene } from "babylonjs/scene";
-
 import { IProperty, AccessorType, CameraType, AnimationChannelTargetPath, AnimationSamplerInterpolation, AccessorComponentType, MaterialAlphaMode, TextureMinFilter, TextureWrapMode, TextureMagFilter, MeshPrimitiveMode } from "babylonjs-gltf2interface";
 import { _IAnimationSamplerData, IGLTF, ISampler, INode, IScene, IMesh, IAccessor, ISkin, ICamera, IAnimation, IAnimationChannel, IAnimationSampler, IBuffer, IBufferView, IMaterialPbrMetallicRoughness, IMaterial, ITextureInfo, ITexture, IImage, IMeshPrimitive, IArrayItem as IArrItem, _ISamplerData } from "./glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "./glTFLoaderExtension";
 import { IGLTFLoader, GLTFFileLoader, GLTFLoaderState, IGLTFLoaderData, GLTFLoaderCoordinateSystemMode, GLTFLoaderAnimationStartMode } from "../glTFFileLoader";
+import { IAnimationKey, AnimationKeyInterpolation } from 'babylonjs/Animations/animationKey';
 
 interface IFileRequestInfo extends IFileRequest {
     _lengthComputable?: boolean;

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.0.0-alpha.19",
+    "version": "4.0.0-alpha.21",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 2 - 1
readme-es6.md

@@ -53,7 +53,8 @@ import { Mesh } from "@babylonjs/core/Meshes/mesh";
 // Side-effects only imports allowing the standard material to be used as default.
 import "@babylonjs/core/Materials/standardMaterial";
 // Side-effects only imports allowing Mesh to create default shapes (to enhance tree shaking, the construction methods on mesh are not available if the meshbuilder has not been imported).
-import "@babylonjs/core/Meshes/meshBuilder";
+import "@babylonjs/core/Meshes/Builders/sphereBuilder";
+import "@babylonjs/core/Meshes/Builders/boxBuilder";
 
 const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
 const engine = new Engine(canvas);

+ 2 - 1
serializers/src/glTF/2.0/glTFAnimation.ts

@@ -3,13 +3,14 @@ import { AnimationSamplerInterpolation, AnimationChannelTargetPath, AccessorType
 import { Nullable } from "babylonjs/types";
 import { Vector3, Quaternion } from "babylonjs/Maths/math";
 import { Tools } from "babylonjs/Misc/tools";
-import { Animation, IAnimationKey, AnimationKeyInterpolation } from "babylonjs/Animations/animation";
+import { Animation } from "babylonjs/Animations/animation";
 import { TransformNode } from "babylonjs/Meshes/transformNode";
 import { Mesh } from "babylonjs/Meshes/mesh";
 import { Scene } from "babylonjs/scene";
 
 import { _BinaryWriter } from "./glTFExporter";
 import { _GLTFUtilities } from "./glTFUtilities";
+import { IAnimationKey, AnimationKeyInterpolation } from 'babylonjs/Animations/animationKey';
 
 /**
  * @hidden

+ 118 - 0
src/Actions/abstractActionManager.ts

@@ -0,0 +1,118 @@
+import { IDisposable } from '../scene';
+import { IActionEvent } from './actionEvent';
+import { IAction } from './action';
+import { Constants } from "../Engines/constants";
+
+/**
+ * Abstract class used to decouple action Manager from scene and meshes.
+ * Do not instantiate.
+ * @see http://doc.babylonjs.com/how_to/how_to_use_actions
+ */
+export abstract class AbstractActionManager implements IDisposable {
+
+    /** Gets the list of active triggers */
+    public static Triggers: { [key: string]: number } = {};
+
+    /** Gets the cursor to use when hovering items */
+    public hoverCursor: string = '';
+
+    /** Gets the list of actions */
+    public actions = new Array<IAction>();
+
+    /**
+     * Releases all associated resources
+     */
+    public abstract dispose(): void;
+
+    /**
+     * Does this action manager has pointer triggers
+     */
+    public abstract get hasPointerTriggers(): boolean;
+
+    /**
+     * Does this action manager has pick triggers
+     */
+    public abstract get hasPickTriggers(): boolean;
+
+    /**
+     * Process a specific trigger
+     * @param trigger defines the trigger to process
+     * @param evt defines the event details to be processed
+     */
+    public abstract processTrigger(trigger: number, evt?: IActionEvent): void;
+
+    /**
+     * Does this action manager handles actions of any of the given triggers
+     * @param triggers defines the triggers to be tested
+     * @return a boolean indicating whether one (or more) of the triggers is handled
+     */
+    public abstract hasSpecificTriggers(triggers: number[]): boolean;
+
+    /**
+     * Does this action manager handles actions of any of the given triggers. This function takes two arguments for
+     * speed.
+     * @param triggerA defines the trigger to be tested
+     * @param triggerB defines the trigger to be tested
+     * @return a boolean indicating whether one (or more) of the triggers is handled
+     */
+    public abstract hasSpecificTriggers2(triggerA: number, triggerB: number): boolean;
+
+    /**
+     * Does this action manager handles actions of a given trigger
+     * @param trigger defines the trigger to be tested
+     * @param parameterPredicate defines an optional predicate to filter triggers by parameter
+     * @return whether the trigger is handled
+     */
+    public abstract hasSpecificTrigger(trigger: number, parameterPredicate?: (parameter: any) => boolean): boolean;
+
+    /**
+         * Serialize this manager to a JSON object
+         * @param name defines the property name to store this manager
+         * @returns a JSON representation of this manager
+         */
+    public abstract serialize(name: string): any;
+
+    /**
+     * Does exist one action manager with at least one trigger
+     **/
+    public static get HasTriggers(): boolean {
+        for (var t in AbstractActionManager.Triggers) {
+            if (AbstractActionManager.Triggers.hasOwnProperty(t)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Does exist one action manager with at least one pick trigger
+     **/
+    public static get HasPickTriggers(): boolean {
+        for (var t in AbstractActionManager.Triggers) {
+            if (AbstractActionManager.Triggers.hasOwnProperty(t)) {
+                let t_int = parseInt(t);
+                if (t_int >= Constants.ACTION_OnPickTrigger && t_int <= Constants.ACTION_OnPickUpTrigger) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Does exist one action manager that handles actions of a given trigger
+     * @param trigger defines the trigger to be tested
+     * @return a boolean indicating whether the trigger is handeled by at least one action manager
+    **/
+    public static HasSpecificTrigger(trigger: number): boolean {
+        for (var t in AbstractActionManager.Triggers) {
+            if (AbstractActionManager.Triggers.hasOwnProperty(t)) {
+                let t_int = parseInt(t);
+                if (t_int === trigger) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+}

+ 33 - 1
src/Actions/action.ts

@@ -12,10 +12,42 @@ declare type Camera = import("../Cameras/camera").Camera;
 declare type Node = import("../node").Node;
 
 /**
+ * Interface used to define Action
+ */
+export interface IAction {
+    /**
+   * Trigger for the action
+   */
+    trigger: number;
+
+    /** the trigger, with or without parameters, for the action */
+    triggerOptions: any;
+
+    /**
+     * Gets the trigger parameters
+     * @returns the trigger parameters
+     */
+    getTriggerParameter(): any;
+
+    /**
+     * Internal only - executes current action event
+     * @hidden
+     */
+    _executeCurrent(evt?: ActionEvent): void;
+
+    /**
+     * Serialize placeholder for child classes
+     * @param parent of child
+     * @returns the serialized object
+     */
+    serialize(parent: any): any;
+}
+
+/**
  * The action to be carried out following a trigger
  * @see http://doc.babylonjs.com/how_to/how_to_use_actions#available-actions
  */
-export class Action {
+export class Action implements IAction {
     /**
      * Trigger for the action
      */

+ 19 - 2
src/Actions/actionEvent.ts

@@ -5,9 +5,27 @@ import { Scene } from "../scene";
 import { Vector2 } from "../Maths/math";
 
 /**
+ * Interface used to define ActionEvent
+ */
+export interface IActionEvent {
+    /** The mesh or sprite that triggered the action */
+    source: any;
+    /** The X mouse cursor position at the time of the event */
+    pointerX: number;
+    /** The Y mouse cursor position at the time of the event */
+    pointerY: number;
+    /** The mesh that is currently pointed at (can be null) */
+    meshUnderPointer: Nullable<AbstractMesh>;
+    /** the original (browser) event that triggered the ActionEvent */
+    sourceEvent?: any;
+    /** additional data for the event */
+    additionalData?: any;
+}
+
+/**
  * ActionEvent is the event being sent when an action is triggered.
  */
-export class ActionEvent {
+export class ActionEvent implements IActionEvent {
     /**
      * Creates a new ActionEvent
      * @param source The mesh or sprite that triggered the action
@@ -30,7 +48,6 @@ export class ActionEvent {
         public sourceEvent?: any,
         /** additional data for the event */
         public additionalData?: any) {
-
     }
 
     /**

+ 7 - 58
src/Actions/actionManager.ts

@@ -7,19 +7,20 @@ import { Condition, ValueCondition } from "./condition";
 import { Action } from "./action";
 import { DoNothingAction } from "./directActions";
 
-import { Constants } from "../Engines/constants";
 import { EngineStore } from "../Engines/engineStore";
-import { ActionEvent } from "../Actions/actionEvent";
+import { IActionEvent } from "../Actions/actionEvent";
 import { Logger } from "../Misc/logger";
 import { DeepCopier } from "../Misc/deepCopier";
 import { _TypeStore } from "../Misc/typeStore";
+import { AbstractActionManager } from './abstractActionManager';
+import { Constants } from "../Engines/constants";
 
 /**
  * Action Manager manages all events to be triggered on a given mesh or the global scene.
  * A single scene can have many Action Managers to handle predefined actions on specific meshes.
  * @see http://doc.babylonjs.com/how_to/how_to_use_actions
  */
-export class ActionManager {
+export class ActionManager extends AbstractActionManager {
     /**
      * Nothing
      * @see http://doc.babylonjs.com/how_to/how_to_use_actions#triggers
@@ -121,16 +122,7 @@ export class ActionManager {
      */
     public static readonly OnKeyUpTrigger = 15;
 
-    /** Gets the list of active triggers */
-    public static Triggers: { [key: string]: number } = {};
-
     // Members
-    /** Gets the list of actions */
-    public actions = new Array<Action>();
-
-    /** Gets the cursor to use when hovering items */
-    public hoverCursor: string = '';
-
     private _scene: Scene;
 
     /**
@@ -138,6 +130,7 @@ export class ActionManager {
      * @param scene defines the hosting scene
      */
     constructor(scene: Scene) {
+        super();
         this._scene = scene || EngineStore.LastCreatedScene;
 
         scene.actionManagers.push(this);
@@ -263,50 +256,6 @@ export class ActionManager {
     }
 
     /**
-     * Does exist one action manager with at least one trigger
-     **/
-    public static get HasTriggers(): boolean {
-        for (var t in ActionManager.Triggers) {
-            if (ActionManager.Triggers.hasOwnProperty(t)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Does exist one action manager with at least one pick trigger
-     **/
-    public static get HasPickTriggers(): boolean {
-        for (var t in ActionManager.Triggers) {
-            if (ActionManager.Triggers.hasOwnProperty(t)) {
-                let t_int = parseInt(t);
-                if (t_int >= ActionManager.OnPickTrigger && t_int <= ActionManager.OnPickUpTrigger) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Does exist one action manager that handles actions of a given trigger
-     * @param trigger defines the trigger to be tested
-     * @return a boolean indicating whether the trigger is handeled by at least one action manager
-    **/
-    public static HasSpecificTrigger(trigger: number): boolean {
-        for (var t in ActionManager.Triggers) {
-            if (ActionManager.Triggers.hasOwnProperty(t)) {
-                let t_int = parseInt(t);
-                if (t_int === trigger) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
      * Registers an action to this action manager
      * @param action defines the action to be registered
      * @return the action amended (prepared) after registration
@@ -358,7 +307,7 @@ export class ActionManager {
      * @param trigger defines the trigger to process
      * @param evt defines the event details to be processed
      */
-    public processTrigger(trigger: number, evt?: ActionEvent): void {
+    public processTrigger(trigger: number, evt?: IActionEvent): void {
         for (var index = 0; index < this.actions.length; index++) {
             var action = this.actions[index];
 
@@ -707,4 +656,4 @@ export class ActionManager {
             default: return "";
         }
     }
-}
+}

+ 571 - 0
src/Animations/animatable.ts

@@ -4,6 +4,10 @@ import { RuntimeAnimation } from "./runtimeAnimation";
 import { Nullable } from "../types";
 import { Observable } from "../Misc/observable";
 import { Scene } from "../scene";
+import { Matrix, Quaternion, Tmp, Vector3 } from '../Maths/math';
+import { PrecisionDate } from '../Misc/precisionDate';
+import { Bone } from '../Bones/bone';
+import { Node } from "../node";
 
 /**
  * Class used to store an actual running animation
@@ -421,3 +425,570 @@ export class Animatable {
         return running;
     }
 }
+
+declare module "../scene" {
+    export interface Scene {
+        /** @hidden */
+        _registerTargetForLateAnimationBinding(runtimeAnimation: RuntimeAnimation, originalValue: any): void;
+
+        /** @hidden */
+        _processLateAnimationBindingsForMatrices(holder: {
+            totalWeight: number,
+            animations: RuntimeAnimation[],
+            originalValue: Matrix
+        }): any;
+
+        /** @hidden */
+        _processLateAnimationBindingsForQuaternions(holder: {
+            totalWeight: number,
+            animations: RuntimeAnimation[],
+            originalValue: Quaternion
+        }, refQuaternion: Quaternion): Quaternion;
+
+        /** @hidden */
+        _processLateAnimationBindings(): void;
+
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param weight defines the weight to apply to the animation (1.0 by default)
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param targetMask defines if the target should be animated if animations are present (this is called recursively on descendant animatables regardless of return value)
+         * @param onAnimationLoop defines the callback to call when an animation loops
+         * @returns the animatable object created for this animation
+         */
+        beginWeightedAnimation(target: any, from: number, to: number, weight: number, loop?: boolean, speedRatio?: number,
+            onAnimationEnd?: () => void, animatable?: Animatable, targetMask?: (target: any) => boolean, onAnimationLoop?: () => void): Animatable;
+
+        /**
+         * Will start the animation sequence of a given target
+         * @param target defines the target
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @param targetMask defines if the target should be animate if animations are present (this is called recursively on descendant animatables regardless of return value)
+         * @param onAnimationLoop defines the callback to call when an animation loops
+         * @returns the animatable object created for this animation
+         */
+        beginAnimation(target: any, from: number, to: number, loop?: boolean, speedRatio?: number,
+            onAnimationEnd?: () => void, animatable?: Animatable, stopCurrent?: boolean,
+            targetMask?: (target: any) => boolean, onAnimationLoop?: () => void): Animatable;
+
+        /**
+         * Will start the animation sequence of a given target and its hierarchy
+         * @param target defines the target
+         * @param directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param from defines from which frame should animation start
+         * @param to defines until which frame should animation run.
+         * @param loop defines if the animation loops
+         * @param speedRatio defines the speed in which to run the animation (1.0 by default)
+         * @param onAnimationEnd defines the function to be executed when the animation ends
+         * @param animatable defines an animatable object. If not provided a new one will be created from the given params
+         * @param stopCurrent defines if the current animations must be stopped first (true by default)
+         * @param targetMask defines if the target should be animated if animations are present (this is called recursively on descendant animatables regardless of return value)
+         * @param onAnimationLoop defines the callback to call when an animation loops
+         * @returns the list of created animatables
+         */
+        beginHierarchyAnimation(target: any, directDescendantsOnly: boolean, from: number, to: number, loop?: boolean, speedRatio?: number,
+            onAnimationEnd?: () => void, animatable?: Animatable, stopCurrent?: boolean,
+            targetMask?: (target: any) => boolean, onAnimationLoop?: () => void): Animatable[];
+
+        /**
+         * Begin a new animation on a given node
+         * @param target defines the target where the animation will take place
+         * @param animations defines the list of animations to start
+         * @param from defines the initial value
+         * @param to defines the final value
+         * @param loop defines if you want animation to loop (off by default)
+         * @param speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @param onAnimationLoop defines the callback to call when an animation loops
+         * @returns the list of created animatables
+         */
+        beginDirectAnimation(target: any, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void, onAnimationLoop?: () => void): Animatable;
+
+        /**
+         * Begin a new animation on a given node and its hierarchy
+         * @param target defines the root node where the animation will take place
+         * @param directDescendantsOnly if true only direct descendants will be used, if false direct and also indirect (children of children, an so on in a recursive manner) descendants will be used.
+         * @param animations defines the list of animations to start
+         * @param from defines the initial value
+         * @param to defines the final value
+         * @param loop defines if you want animation to loop (off by default)
+         * @param speedRatio defines the speed ratio to apply to all animations
+         * @param onAnimationEnd defines the callback to call when an animation ends (will be called once per node)
+         * @param onAnimationLoop defines the callback to call when an animation loops
+         * @returns the list of animatables created for all nodes
+         */
+        beginDirectHierarchyAnimation(target: Node, directDescendantsOnly: boolean, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void, onAnimationLoop?: () => void): Animatable[];
+
+        /**
+         * Gets the animatable associated with a specific target
+         * @param target defines the target of the animatable
+         * @returns the required animatable if found
+         */
+        getAnimatableByTarget(target: any): Nullable<Animatable>;
+
+        /**
+         * Gets all animatables associated with a given target
+         * @param target defines the target to look animatables for
+         * @returns an array of Animatables
+         */
+        getAllAnimatablesByTarget(target: any): Array<Animatable>;
+
+        /**
+         * Will stop the animation of the given target
+         * @param target - the target
+         * @param animationName - the name of the animation to stop (all animations will be stopped if both this and targetMask are empty)
+         * @param targetMask - a function that determines if the animation should be stopped based on its target (all animations will be stopped if both this and animationName are empty)
+         */
+        stopAnimation(target: any, animationName?: string, targetMask?: (target: any) => boolean): void;
+
+        /**
+        * Stops and removes all animations that have been applied to the scene
+        */
+        stopAllAnimations(): void;
+    }
+}
+
+Scene.prototype._animate = function(): void {
+    if (!this.animationsEnabled || this._activeAnimatables.length === 0) {
+        return;
+    }
+
+    // Getting time
+    var now = PrecisionDate.Now;
+    if (!this._animationTimeLast) {
+        if (this._pendingData.length > 0) {
+            return;
+        }
+        this._animationTimeLast = now;
+    }
+    var deltaTime = this.useConstantAnimationDeltaTime ? 16.0 : (now - this._animationTimeLast) * this.animationTimeScale;
+    this._animationTime += deltaTime;
+    this._animationTimeLast = now;
+    for (var index = 0; index < this._activeAnimatables.length; index++) {
+        this._activeAnimatables[index]._animate(this._animationTime);
+    }
+
+    // Late animation bindings
+    this._processLateAnimationBindings();
+};
+
+Scene.prototype.beginWeightedAnimation = function(target: any, from: number, to: number, weight = 1.0, loop?: boolean, speedRatio: number = 1.0,
+    onAnimationEnd?: () => void, animatable?: Animatable, targetMask?: (target: any) => boolean, onAnimationLoop?: () => void): Animatable {
+
+    let returnedAnimatable = this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, false, targetMask, onAnimationLoop);
+    returnedAnimatable.weight = weight;
+
+    return returnedAnimatable;
+};
+
+Scene.prototype.beginAnimation = function(target: any, from: number, to: number, loop?: boolean, speedRatio: number = 1.0,
+    onAnimationEnd?: () => void, animatable?: Animatable, stopCurrent = true,
+    targetMask?: (target: any) => boolean, onAnimationLoop?: () => void): Animatable {
+
+    if (from > to && speedRatio > 0) {
+        speedRatio *= -1;
+    }
+
+    if (stopCurrent) {
+        this.stopAnimation(target, undefined, targetMask);
+    }
+
+    if (!animatable) {
+        animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, undefined, onAnimationLoop);
+    }
+
+    const shouldRunTargetAnimations = targetMask ? targetMask(target) : true;
+    // Local animations
+    if (target.animations && shouldRunTargetAnimations) {
+        animatable.appendAnimations(target, target.animations);
+    }
+
+    // Children animations
+    if (target.getAnimatables) {
+        var animatables = target.getAnimatables();
+        for (var index = 0; index < animatables.length; index++) {
+            this.beginAnimation(animatables[index], from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask, onAnimationLoop);
+        }
+    }
+
+    animatable.reset();
+
+    return animatable;
+};
+
+Scene.prototype.beginHierarchyAnimation = function(target: any, directDescendantsOnly: boolean, from: number, to: number, loop?: boolean, speedRatio: number = 1.0,
+    onAnimationEnd?: () => void, animatable?: Animatable, stopCurrent = true,
+    targetMask?: (target: any) => boolean, onAnimationLoop?: () => void): Animatable[] {
+
+    let children = target.getDescendants(directDescendantsOnly);
+
+    let result = [];
+    result.push(this.beginAnimation(target, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask));
+    for (var child of children) {
+        result.push(this.beginAnimation(child, from, to, loop, speedRatio, onAnimationEnd, animatable, stopCurrent, targetMask));
+    }
+
+    return result;
+};
+
+Scene.prototype.beginDirectAnimation = function(target: any, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void, onAnimationLoop?: () => void): Animatable {
+    if (speedRatio === undefined) {
+        speedRatio = 1.0;
+    }
+
+    var animatable = new Animatable(this, target, from, to, loop, speedRatio, onAnimationEnd, animations, onAnimationLoop);
+
+    return animatable;
+};
+
+Scene.prototype.beginDirectHierarchyAnimation = function(target: Node, directDescendantsOnly: boolean, animations: Animation[], from: number, to: number, loop?: boolean, speedRatio?: number, onAnimationEnd?: () => void, onAnimationLoop?: () => void): Animatable[] {
+    let children = target.getDescendants(directDescendantsOnly);
+
+    let result = [];
+    result.push(this.beginDirectAnimation(target, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop));
+    for (var child of children) {
+        result.push(this.beginDirectAnimation(child, animations, from, to, loop, speedRatio, onAnimationEnd, onAnimationLoop));
+    }
+
+    return result;
+};
+
+Scene.prototype.getAnimatableByTarget = function(target: any): Nullable<Animatable> {
+    for (var index = 0; index < this._activeAnimatables.length; index++) {
+        if (this._activeAnimatables[index].target === target) {
+            return this._activeAnimatables[index];
+        }
+    }
+
+    return null;
+};
+
+Scene.prototype.getAllAnimatablesByTarget = function(target: any): Array<Animatable> {
+    let result = [];
+    for (var index = 0; index < this._activeAnimatables.length; index++) {
+        if (this._activeAnimatables[index].target === target) {
+            result.push(this._activeAnimatables[index]);
+        }
+    }
+
+    return result;
+};
+
+/**
+ * Will stop the animation of the given target
+ * @param target - the target
+ * @param animationName - the name of the animation to stop (all animations will be stopped if both this and targetMask are empty)
+ * @param targetMask - a function that determines if the animation should be stopped based on its target (all animations will be stopped if both this and animationName are empty)
+ */
+Scene.prototype.stopAnimation = function(target: any, animationName?: string, targetMask?: (target: any) => boolean): void {
+    var animatables = this.getAllAnimatablesByTarget(target);
+
+    for (var animatable of animatables) {
+        animatable.stop(animationName, targetMask);
+    }
+};
+
+/**
+ * Stops and removes all animations that have been applied to the scene
+ */
+Scene.prototype.stopAllAnimations = function(): void {
+    if (this._activeAnimatables) {
+        for (let i = 0; i < this._activeAnimatables.length; i++) {
+            this._activeAnimatables[i].stop();
+        }
+        this._activeAnimatables = [];
+    }
+
+    for (var group of this.animationGroups) {
+        group.stop();
+    }
+};
+
+Scene.prototype._registerTargetForLateAnimationBinding = function(runtimeAnimation: RuntimeAnimation, originalValue: any): void {
+    let target = runtimeAnimation.target;
+    this._registeredForLateAnimationBindings.pushNoDuplicate(target);
+
+    if (!target._lateAnimationHolders) {
+        target._lateAnimationHolders = {};
+    }
+
+    if (!target._lateAnimationHolders[runtimeAnimation.targetPath]) {
+        target._lateAnimationHolders[runtimeAnimation.targetPath] = {
+            totalWeight: 0,
+            animations: [],
+            originalValue: originalValue
+        };
+    }
+
+    target._lateAnimationHolders[runtimeAnimation.targetPath].animations.push(runtimeAnimation);
+    target._lateAnimationHolders[runtimeAnimation.targetPath].totalWeight += runtimeAnimation.weight;
+};
+
+Scene.prototype._processLateAnimationBindingsForMatrices = function(holder: {
+    totalWeight: number,
+    animations: RuntimeAnimation[],
+    originalValue: Matrix
+}): any {
+    let normalizer = 1.0;
+    let finalPosition = Tmp.Vector3[0];
+    let finalScaling = Tmp.Vector3[1];
+    let finalQuaternion = Tmp.Quaternion[0];
+    let startIndex = 0;
+    let originalAnimation = holder.animations[0];
+    let originalValue = holder.originalValue;
+
+    var scale = 1;
+    if (holder.totalWeight < 1.0) {
+        // We need to mix the original value in
+        originalValue.decompose(finalScaling, finalQuaternion, finalPosition);
+        scale = 1.0 - holder.totalWeight;
+    } else {
+        startIndex = 1;
+        // We need to normalize the weights
+        normalizer = holder.totalWeight;
+        originalAnimation.currentValue.decompose(finalScaling, finalQuaternion, finalPosition);
+        scale = originalAnimation.weight / normalizer;
+        if (scale == 1) {
+            return originalAnimation.currentValue;
+        }
+    }
+
+    finalScaling.scaleInPlace(scale);
+    finalPosition.scaleInPlace(scale);
+    finalQuaternion.scaleInPlace(scale);
+
+    for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+        var runtimeAnimation = holder.animations[animIndex];
+        var scale = runtimeAnimation.weight / normalizer;
+        let currentPosition = Tmp.Vector3[2];
+        let currentScaling = Tmp.Vector3[3];
+        let currentQuaternion = Tmp.Quaternion[1];
+
+        runtimeAnimation.currentValue.decompose(currentScaling, currentQuaternion, currentPosition);
+        currentScaling.scaleAndAddToRef(scale, finalScaling);
+        currentQuaternion.scaleAndAddToRef(scale, finalQuaternion);
+        currentPosition.scaleAndAddToRef(scale, finalPosition);
+    }
+
+    Matrix.ComposeToRef(finalScaling, finalQuaternion, finalPosition, originalAnimation._workValue);
+    return originalAnimation._workValue;
+};
+
+Scene.prototype._processLateAnimationBindingsForQuaternions = function(holder: {
+    totalWeight: number,
+    animations: RuntimeAnimation[],
+    originalValue: Quaternion
+}, refQuaternion: Quaternion): Quaternion {
+    let originalAnimation = holder.animations[0];
+    let originalValue = holder.originalValue;
+
+    if (holder.animations.length === 1) {
+        Quaternion.SlerpToRef(originalValue, originalAnimation.currentValue, Math.min(1.0, holder.totalWeight), refQuaternion);
+        return refQuaternion;
+    }
+
+    let normalizer = 1.0;
+    let quaternions: Array<Quaternion>;
+    let weights: Array<number>;
+
+    if (holder.totalWeight < 1.0) {
+        let scale = 1.0 - holder.totalWeight;
+
+        quaternions = [];
+        weights = [];
+
+        quaternions.push(originalValue);
+        weights.push(scale);
+    } else {
+        if (holder.animations.length === 2) { // Slerp as soon as we can
+            Quaternion.SlerpToRef(holder.animations[0].currentValue, holder.animations[1].currentValue, holder.animations[1].weight / holder.totalWeight, refQuaternion);
+            return refQuaternion;
+        }
+        quaternions = [];
+        weights = [];
+
+        normalizer = holder.totalWeight;
+    }
+    for (var animIndex = 0; animIndex < holder.animations.length; animIndex++) {
+        let runtimeAnimation = holder.animations[animIndex];
+        quaternions.push(runtimeAnimation.currentValue);
+        weights.push(runtimeAnimation.weight / normalizer);
+    }
+
+    // https://gamedev.stackexchange.com/questions/62354/method-for-interpolation-between-3-quaternions
+
+    let cumulativeAmount = 0;
+    let cumulativeQuaternion: Nullable<Quaternion> = null;
+    for (var index = 0; index < quaternions.length;) {
+        if (!cumulativeQuaternion) {
+            Quaternion.SlerpToRef(quaternions[index], quaternions[index + 1], weights[index + 1] / (weights[index] + weights[index + 1]), refQuaternion);
+            cumulativeQuaternion = refQuaternion;
+            cumulativeAmount = weights[index] + weights[index + 1];
+            index += 2;
+            continue;
+        }
+        cumulativeAmount += weights[index];
+        Quaternion.SlerpToRef(cumulativeQuaternion, quaternions[index], weights[index] / cumulativeAmount, cumulativeQuaternion);
+        index++;
+    }
+
+    return cumulativeQuaternion!;
+};
+
+Scene.prototype._processLateAnimationBindings = function(): void {
+    if (!this._registeredForLateAnimationBindings.length) {
+        return;
+    }
+    for (var index = 0; index < this._registeredForLateAnimationBindings.length; index++) {
+        var target = this._registeredForLateAnimationBindings.data[index];
+
+        for (var path in target._lateAnimationHolders) {
+            var holder = target._lateAnimationHolders[path];
+            let originalAnimation: RuntimeAnimation = holder.animations[0];
+            let originalValue = holder.originalValue;
+
+            let matrixDecomposeMode = Animation.AllowMatrixDecomposeForInterpolation && originalValue.m; // ie. data is matrix
+
+            let finalValue: any = target[path];
+            if (matrixDecomposeMode) {
+                finalValue = this._processLateAnimationBindingsForMatrices(holder);
+            } else {
+                let quaternionMode = originalValue.w !== undefined;
+                if (quaternionMode) {
+                    finalValue = this._processLateAnimationBindingsForQuaternions(holder, finalValue || Quaternion.Identity());
+                } else {
+
+                    let startIndex = 0;
+                    let normalizer = 1.0;
+
+                    if (holder.totalWeight < 1.0) {
+                        // We need to mix the original value in
+                        if (originalValue.scale) {
+                            finalValue = originalValue.scale(1.0 - holder.totalWeight);
+                        } else {
+                            finalValue = originalValue * (1.0 - holder.totalWeight);
+                        }
+                    } else {
+                        // We need to normalize the weights
+                        normalizer = holder.totalWeight;
+                        let scale = originalAnimation.weight / normalizer;
+                        if (scale !== 1) {
+                            if (originalAnimation.currentValue.scale) {
+                                finalValue = originalAnimation.currentValue.scale(scale);
+                            } else {
+                                finalValue = originalAnimation.currentValue * scale;
+                            }
+                        } else {
+                            finalValue = originalAnimation.currentValue;
+                        }
+
+                        startIndex = 1;
+                    }
+
+                    for (var animIndex = startIndex; animIndex < holder.animations.length; animIndex++) {
+                        var runtimeAnimation = holder.animations[animIndex];
+                        var scale = runtimeAnimation.weight / normalizer;
+                        if (runtimeAnimation.currentValue.scaleAndAddToRef) {
+                            runtimeAnimation.currentValue.scaleAndAddToRef(scale, finalValue);
+                        } else {
+                            finalValue += runtimeAnimation.currentValue * scale;
+                        }
+                    }
+                }
+            }
+            target[path] = finalValue;
+        }
+
+        target._lateAnimationHolders = {};
+    }
+    this._registeredForLateAnimationBindings.reset();
+};
+
+declare module "../Bones/bone" {
+    export interface Bone {
+        /**
+         * Copy an animation range from another bone
+         * @param source defines the source bone
+         * @param rangeName defines the range name to copy
+         * @param frameOffset defines the frame offset
+         * @param rescaleAsRequired defines if rescaling must be applied if required
+         * @param skelDimensionsRatio defines the scaling ratio
+         * @returns true if operation was successful
+         */
+        copyAnimationRange(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired: boolean, skelDimensionsRatio: Nullable<Vector3>): boolean;
+    }
+}
+
+Bone.prototype.copyAnimationRange = function(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired = false, skelDimensionsRatio: Nullable<Vector3> = null): boolean {
+    // all animation may be coming from a library skeleton, so may need to create animation
+    if (this.animations.length === 0) {
+        this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
+        this.animations[0].setKeys([]);
+    }
+
+    // get animation info / verify there is such a range from the source bone
+    var sourceRange = source.animations[0].getRange(rangeName);
+    if (!sourceRange) {
+        return false;
+    }
+    var from = sourceRange.from;
+    var to = sourceRange.to;
+    var sourceKeys = source.animations[0].getKeys();
+
+    // rescaling prep
+    var sourceBoneLength = source.length;
+    var sourceParent = source.getParent();
+    var parent = this.getParent();
+    var parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
+    var parentRatio = parentScalingReqd && parent && sourceParent ? parent.length / sourceParent.length : 1;
+
+    var dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);
+
+    var destKeys = this.animations[0].getKeys();
+
+    // loop vars declaration
+    var orig: { frame: number, value: Matrix };
+    var origTranslation: Vector3;
+    var mat: Matrix;
+
+    for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
+        orig = sourceKeys[key];
+        if (orig.frame >= from && orig.frame <= to) {
+            if (rescaleAsRequired) {
+                mat = orig.value.clone();
+
+                // scale based on parent ratio, when bone has parent
+                if (parentScalingReqd) {
+                    origTranslation = mat.getTranslation();
+                    mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
+
+                    // scale based on skeleton dimension ratio when root bone, and value is passed
+                } else if (dimensionsScalingReqd && skelDimensionsRatio) {
+                    origTranslation = mat.getTranslation();
+                    mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));
+
+                    // use original when root bone, and no data for skelDimensionsRatio
+                } else {
+                    mat = orig.value;
+                }
+            } else {
+                mat = orig.value;
+            }
+            destKeys.push({ frame: orig.frame + frameOffset, value: mat });
+        }
+    }
+    this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
+    return true;
+};

+ 7 - 211
src/Animations/animation.ts

@@ -1,225 +1,21 @@
 import { IEasingFunction, EasingFunction } from "./easing";
-import { Path2, Vector3, Quaternion, Vector2, Color3, Size, Matrix } from "../Maths/math";
+import { Vector3, Quaternion, Vector2, Color3, Size, Matrix } from "../Maths/math";
 import { Scalar } from "../Maths/math.scalar";
 
 import { Nullable } from "../types";
 import { Scene } from "../scene";
 import { IAnimatable } from "../Misc/tools";
-import { Node } from "../node";
-import { Texture } from "../Materials/Textures/texture";
 import { SerializationHelper } from "../Misc/decorators";
+import { _TypeStore } from '../Misc/typeStore';
+import { IAnimationKey, AnimationKeyInterpolation } from './animationKey';
+import { AnimationRange } from './animationRange';
+import { AnimationEvent } from './animationEvent';
+import { Node } from "../node";
 
 declare type Animatable = import("./animatable").Animatable;
 declare type RuntimeAnimation = import("./runtimeAnimation").RuntimeAnimation;
 
 /**
- * Represents the range of an animation
- */
-export class AnimationRange {
-    /**
-     * Initializes the range of an animation
-     * @param name The name of the animation range
-     * @param from The starting frame of the animation
-     * @param to The ending frame of the animation
-     */
-    constructor(
-        /**The name of the animation range**/
-        public name: string,
-        /**The starting frame of the animation */
-        public from: number,
-        /**The ending frame of the animation*/
-        public to: number) {
-    }
-
-    /**
-     * Makes a copy of the animation range
-     * @returns A copy of the animation range
-     */
-    public clone(): AnimationRange {
-        return new AnimationRange(this.name, this.from, this.to);
-    }
-}
-
-/**
- * Composed of a frame, and an action function
- */
-export class AnimationEvent {
-    /**
-     * Specifies if the animation event is done
-     */
-    public isDone: boolean = false;
-
-    /**
-     * Initializes the animation event
-     * @param frame The frame for which the event is triggered
-     * @param action The event to perform when triggered
-     * @param onlyOnce Specifies if the event should be triggered only once
-     */
-    constructor(
-        /** The frame for which the event is triggered **/
-        public frame: number,
-        /** The event to perform when triggered **/
-        public action: (currentFrame: number) => void,
-        /** Specifies if the event should be triggered only once**/
-        public onlyOnce?: boolean) {
-    }
-
-    /** @hidden */
-    public _clone(): AnimationEvent {
-        return new AnimationEvent(this.frame, this.action, this.onlyOnce);
-    }
-}
-
-/**
- * A cursor which tracks a point on a path
- */
-export class PathCursor {
-    /**
-     * Stores path cursor callbacks for when an onchange event is triggered
-     */
-    private _onchange = new Array<(cursor: PathCursor) => void>();
-
-    /**
-     * The value of the path cursor
-     */
-    value: number = 0;
-
-    /**
-     * The animation array of the path cursor
-     */
-    animations = new Array<Animation>();
-
-    /**
-     * Initializes the path cursor
-     * @param path The path to track
-     */
-    constructor(private path: Path2) {
-    }
-
-    /**
-     * Gets the cursor point on the path
-     * @returns A point on the path cursor at the cursor location
-     */
-    public getPoint(): Vector3 {
-        var point = this.path.getPointAtLengthPosition(this.value);
-        return new Vector3(point.x, 0, point.y);
-    }
-
-    /**
-     * Moves the cursor ahead by the step amount
-     * @param step The amount to move the cursor forward
-     * @returns This path cursor
-     */
-    public moveAhead(step: number = 0.002): PathCursor {
-        this.move(step);
-
-        return this;
-    }
-
-    /**
-     * Moves the cursor behind by the step amount
-     * @param step The amount to move the cursor back
-     * @returns This path cursor
-     */
-    public moveBack(step: number = 0.002): PathCursor {
-        this.move(-step);
-
-        return this;
-    }
-
-    /**
-     * Moves the cursor by the step amount
-     * If the step amount is greater than one, an exception is thrown
-     * @param step The amount to move the cursor
-     * @returns This path cursor
-     */
-    public move(step: number): PathCursor {
-
-        if (Math.abs(step) > 1) {
-            throw "step size should be less than 1.";
-        }
-
-        this.value += step;
-        this.ensureLimits();
-        this.raiseOnChange();
-
-        return this;
-    }
-
-    /**
-     * Ensures that the value is limited between zero and one
-     * @returns This path cursor
-     */
-    private ensureLimits(): PathCursor {
-        while (this.value > 1) {
-            this.value -= 1;
-        }
-        while (this.value < 0) {
-            this.value += 1;
-        }
-
-        return this;
-    }
-
-    /**
-     * Runs onchange callbacks on change (used by the animation engine)
-     * @returns This path cursor
-     */
-    private raiseOnChange(): PathCursor {
-        this._onchange.forEach((f) => f(this));
-
-        return this;
-    }
-
-    /**
-     * Executes a function on change
-     * @param f A path cursor onchange callback
-     * @returns This path cursor
-     */
-    public onchange(f: (cursor: PathCursor) => void): PathCursor {
-        this._onchange.push(f);
-
-        return this;
-    }
-}
-
-/**
- * Defines an interface which represents an animation key frame
- */
-export interface IAnimationKey {
-    /**
-     * Frame of the key frame
-     */
-    frame: number;
-    /**
-     * Value at the specifies key frame
-     */
-    value: any;
-    /**
-     * The input tangent for the cubic hermite spline
-     */
-    inTangent?: any;
-    /**
-     * The output tangent for the cubic hermite spline
-     */
-    outTangent?: any;
-    /**
-     * The animation interpolation type
-     */
-    interpolation?: AnimationKeyInterpolation;
-}
-
-/**
- * Enum for the animation key frame interpolation type
- */
-export enum AnimationKeyInterpolation {
-    /**
-     * Do not interpolate between keys and use the start key value only. Tangents are ignored
-     */
-    STEP = 1
-}
-
-/**
  * Class used to store any kind of animation
  */
 export class Animation {
@@ -1238,5 +1034,5 @@ export class Animation {
     }
 }
 
-Texture._AnimationParser = Animation.Parse;
+_TypeStore.RegisteredTypes["BABYLON.Animation"] = Animation;
 Node._AnimationRangeFactory = (name: string, from: number, to: number) => new AnimationRange(name, from, to);

+ 29 - 0
src/Animations/animationEvent.ts

@@ -0,0 +1,29 @@
+/**
+ * Composed of a frame, and an action function
+ */
+export class AnimationEvent {
+    /**
+     * Specifies if the animation event is done
+     */
+    public isDone: boolean = false;
+
+    /**
+     * Initializes the animation event
+     * @param frame The frame for which the event is triggered
+     * @param action The event to perform when triggered
+     * @param onlyOnce Specifies if the event should be triggered only once
+     */
+    constructor(
+        /** The frame for which the event is triggered **/
+        public frame: number,
+        /** The event to perform when triggered **/
+        public action: (currentFrame: number) => void,
+        /** Specifies if the event should be triggered only once**/
+        public onlyOnce?: boolean) {
+    }
+
+    /** @hidden */
+    public _clone(): AnimationEvent {
+        return new AnimationEvent(this.frame, this.action, this.onlyOnce);
+    }
+}

+ 2 - 1
src/Animations/animationGroup.ts

@@ -1,5 +1,6 @@
 import { Animatable } from "./animatable";
-import { IAnimationKey, Animation } from "./animation";
+import { Animation } from "./animation";
+import { IAnimationKey } from "./animationKey";
 
 import { Scene, IDisposable } from "../scene";
 import { Observable } from "../Misc/observable";

+ 35 - 0
src/Animations/animationKey.ts

@@ -0,0 +1,35 @@
+/**
+ * Defines an interface which represents an animation key frame
+ */
+export interface IAnimationKey {
+    /**
+     * Frame of the key frame
+     */
+    frame: number;
+    /**
+     * Value at the specifies key frame
+     */
+    value: any;
+    /**
+     * The input tangent for the cubic hermite spline
+     */
+    inTangent?: any;
+    /**
+     * The output tangent for the cubic hermite spline
+     */
+    outTangent?: any;
+    /**
+     * The animation interpolation type
+     */
+    interpolation?: AnimationKeyInterpolation;
+}
+
+/**
+ * Enum for the animation key frame interpolation type
+ */
+export enum AnimationKeyInterpolation {
+    /**
+     * Do not interpolate between keys and use the start key value only. Tangents are ignored
+     */
+    STEP = 1
+}

+ 27 - 0
src/Animations/animationRange.ts

@@ -0,0 +1,27 @@
+/**
+ * Represents the range of an animation
+ */
+export class AnimationRange {
+    /**
+     * Initializes the range of an animation
+     * @param name The name of the animation range
+     * @param from The starting frame of the animation
+     * @param to The ending frame of the animation
+     */
+    constructor(
+        /**The name of the animation range**/
+        public name: string,
+        /**The starting frame of the animation */
+        public from: number,
+        /**The ending frame of the animation*/
+        public to: number) {
+    }
+
+    /**
+     * Makes a copy of the animation range
+     * @returns A copy of the animation range
+     */
+    public clone(): AnimationRange {
+        return new AnimationRange(this.name, this.from, this.to);
+    }
+}

+ 5 - 1
src/Animations/index.ts

@@ -3,4 +3,8 @@ export * from "./animation";
 export * from "./animationGroup";
 export * from "./animationPropertiesOverride";
 export * from "./easing";
-export * from "./runtimeAnimation";
+export * from "./runtimeAnimation";
+export * from "./animationEvent";
+export * from "./animationGroup";
+export * from "./animationKey";
+export * from "./animationRange";

+ 114 - 0
src/Animations/pathCursor.ts

@@ -0,0 +1,114 @@
+import { Path2, Vector3 } from '../Maths/math';
+
+/**
+ * A cursor which tracks a point on a path
+ */
+export class PathCursor {
+    /**
+     * Stores path cursor callbacks for when an onchange event is triggered
+     */
+    private _onchange = new Array<(cursor: PathCursor) => void>();
+
+    /**
+     * The value of the path cursor
+     */
+    value: number = 0;
+
+    /**
+     * The animation array of the path cursor
+     */
+    animations = new Array<Animation>();
+
+    /**
+     * Initializes the path cursor
+     * @param path The path to track
+     */
+    constructor(private path: Path2) {
+    }
+
+    /**
+     * Gets the cursor point on the path
+     * @returns A point on the path cursor at the cursor location
+     */
+    public getPoint(): Vector3 {
+        var point = this.path.getPointAtLengthPosition(this.value);
+        return new Vector3(point.x, 0, point.y);
+    }
+
+    /**
+     * Moves the cursor ahead by the step amount
+     * @param step The amount to move the cursor forward
+     * @returns This path cursor
+     */
+    public moveAhead(step: number = 0.002): PathCursor {
+        this.move(step);
+
+        return this;
+    }
+
+    /**
+     * Moves the cursor behind by the step amount
+     * @param step The amount to move the cursor back
+     * @returns This path cursor
+     */
+    public moveBack(step: number = 0.002): PathCursor {
+        this.move(-step);
+
+        return this;
+    }
+
+    /**
+     * Moves the cursor by the step amount
+     * If the step amount is greater than one, an exception is thrown
+     * @param step The amount to move the cursor
+     * @returns This path cursor
+     */
+    public move(step: number): PathCursor {
+
+        if (Math.abs(step) > 1) {
+            throw "step size should be less than 1.";
+        }
+
+        this.value += step;
+        this.ensureLimits();
+        this.raiseOnChange();
+
+        return this;
+    }
+
+    /**
+     * Ensures that the value is limited between zero and one
+     * @returns This path cursor
+     */
+    private ensureLimits(): PathCursor {
+        while (this.value > 1) {
+            this.value -= 1;
+        }
+        while (this.value < 0) {
+            this.value += 1;
+        }
+
+        return this;
+    }
+
+    /**
+     * Runs onchange callbacks on change (used by the animation engine)
+     * @returns This path cursor
+     */
+    private raiseOnChange(): PathCursor {
+        this._onchange.forEach((f) => f(this));
+
+        return this;
+    }
+
+    /**
+     * Executes a function on change
+     * @param f A path cursor onchange callback
+     * @returns This path cursor
+     */
+    public onchange(f: (cursor: PathCursor) => void): PathCursor {
+        this._onchange.push(f);
+
+        return this;
+    }
+}

+ 2 - 1
src/Animations/runtimeAnimation.ts

@@ -1,6 +1,7 @@
 import { DeepImmutable } from "../types";
 import { Quaternion, Vector3, Vector2, Size, Color3, Matrix } from "../Maths/math";
-import { Animation, AnimationEvent } from "./animation";
+import { Animation } from "./animation";
+import { AnimationEvent } from "./animationEvent";
 
 declare type Animatable = import("./animatable").Animatable;
 

+ 1 - 1
src/Audio/audioEngine.ts

@@ -267,7 +267,7 @@ export class AudioEngine implements IAudioEngine {
     }
 
     private _displayMuteButton() {
-        if (this.useCustomUnlockedButton) {
+        if (this.useCustomUnlockedButton || this._muteButton) {
             return;
         }
 

+ 40 - 12
src/Audio/sound.ts

@@ -7,6 +7,7 @@ import { Engine } from "../Engines/engine";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { TransformNode } from "../Meshes/transformNode";
 import { Logger } from "../Misc/logger";
+import { _DevTools } from '../Misc/devTools';
 
 /**
  * Defines a sound that can be played in the application.
@@ -115,7 +116,7 @@ export class Sound {
 
     /** @hidden */
     public static _SceneComponentInitialization: (scene: Scene) => void = (_) => {
-        throw "Import AudioSceneComponent before creating sound.";
+        throw _DevTools.WarnImport("AudioSceneComponent");
     }
 
     /**
@@ -655,30 +656,57 @@ export class Sound {
                                 // In browsers that don’t yet support this functionality,
                                 // playPromise won’t be defined.
                                 if (playPromise !== undefined) {
-                                    playPromise.catch(function(error) {
+                                    playPromise.catch((error) => {
                                         // Automatic playback failed.
                                         // Waiting for the audio engine to be unlocked by user click on unmute
                                         Engine.audioEngine.lock();
-                                        Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                        if (this.loop || this.autoplay) {
+                                            Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                        }
                                     });
                                 }
                             }
                             else {
-                                Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                if (this.loop || this.autoplay) {
+                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                }
                             }
                         };
                         tryToPlay();
                     }
                 }
                 else {
-                    this._soundSource = Engine.audioEngine.audioContext.createBufferSource();
-                    this._soundSource.buffer = this._audioBuffer;
-                    this._soundSource.connect(this._inputAudioNode);
-                    this._soundSource.loop = this.loop;
-                    this._soundSource.playbackRate.value = this._playbackRate;
-                    this._soundSource.onended = () => { this._onended(); };
-                    if (this._soundSource.buffer) {
-                        this._soundSource.start(startTime, this.isPaused ? this._startOffset % this._soundSource.buffer.duration : offset ? offset : 0);
+                    var tryToPlay = () => {
+                        if (Engine.audioEngine.audioContext) {
+                            this._soundSource = Engine.audioEngine.audioContext.createBufferSource();
+                            this._soundSource.buffer = this._audioBuffer;
+                            this._soundSource.connect(this._inputAudioNode);
+                            this._soundSource.loop = this.loop;
+                            this._soundSource.playbackRate.value = this._playbackRate;
+                            this._soundSource.onended = () => { this._onended(); };
+                            startTime = time ? Engine.audioEngine.audioContext!.currentTime + time : Engine.audioEngine.audioContext!.currentTime;
+                            this._soundSource!.start(startTime, this.isPaused ? this._startOffset % this._soundSource!.buffer!.duration : offset ? offset : 0);
+                        }
+                    };
+
+                    if (Engine.audioEngine.audioContext.state === "suspended") {
+                        // Wait a bit for FF as context seems late to be ready.
+                        setTimeout(() => {
+                            if (Engine.audioEngine.audioContext!.state === "suspended") {
+                                // Automatic playback failed.
+                                // Waiting for the audio engine to be unlocked by user click on unmute
+                                Engine.audioEngine.lock();
+                                if (this.loop || this.autoplay) {
+                                    Engine.audioEngine.onAudioUnlockedObservable.addOnce(() => { tryToPlay(); });
+                                }
+                            }
+                            else {
+                                tryToPlay();
+                            }
+                        }, 500);
+                    }
+                    else {
+                        tryToPlay();
                     }
                 }
                 this._startTime = startTime;

+ 5 - 0
src/Behaviors/Meshes/pointerDragBehavior.ts

@@ -9,6 +9,8 @@ import { PointerInfo, PointerEventTypes } from "../../Events/pointerEvents";
 import { Ray } from "../../Culling/ray";
 import { PivotTools } from '../../Misc/pivotTools';
 
+import "../../Meshes/Builders/planeBuilder";
+
 /**
  * A behavior that when attached to a mesh will allow the mesh to be dragged around the screen based on pointer events
  */
@@ -400,6 +402,9 @@ export class PointerDragBehavior implements Behavior<AbstractMesh> {
             this._dragPlane.position.copyFrom(this._pointA);
             this._dragPlane.lookAt(ray.origin);
         }
+        // Update the position of the drag plane so it doesn't get out of sync with the node (eg. when moving back and forth quickly)
+        this._dragPlane.position.copyFrom(this._attachedNode.absolutePosition);
+
         this._dragPlane.computeWorldMatrix(true);
     }
 

+ 3 - 73
src/Bones/bone.ts

@@ -3,12 +3,13 @@ import { Skeleton } from "./skeleton";
 import { Vector3, Quaternion, Matrix, Space } from "../Maths/math";
 import { ArrayTools } from "../Misc/arrayTools";
 import { Nullable } from "../types";
-import { Animation } from "../Animations/animation";
-import { AnimationPropertiesOverride } from "../Animations/animationPropertiesOverride";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { TransformNode } from "../Meshes/transformNode";
 import { Node } from "../node";
 
+declare type Animation = import("../Animations/animation").Animation;
+declare type AnimationPropertiesOverride = import("../Animations/animationPropertiesOverride").AnimationPropertiesOverride;
+
 /**
  * Class used to store bone information
  * @see http://doc.babylonjs.com/how_to/how_to_use_bones_and_skeletons
@@ -363,77 +364,6 @@ export class Bone extends Node {
     }
 
     /**
-     * Copy an animation range from another bone
-     * @param source defines the source bone
-     * @param rangeName defines the range name to copy
-     * @param frameOffset defines the frame offset
-     * @param rescaleAsRequired defines if rescaling must be applied if required
-     * @param skelDimensionsRatio defines the scaling ratio
-     * @returns true if operation was successful
-     */
-    public copyAnimationRange(source: Bone, rangeName: string, frameOffset: number, rescaleAsRequired = false, skelDimensionsRatio: Nullable<Vector3> = null): boolean {
-        // all animation may be coming from a library skeleton, so may need to create animation
-        if (this.animations.length === 0) {
-            this.animations.push(new Animation(this.name, "_matrix", source.animations[0].framePerSecond, Animation.ANIMATIONTYPE_MATRIX, 0));
-            this.animations[0].setKeys([]);
-        }
-
-        // get animation info / verify there is such a range from the source bone
-        var sourceRange = source.animations[0].getRange(rangeName);
-        if (!sourceRange) {
-            return false;
-        }
-        var from = sourceRange.from;
-        var to = sourceRange.to;
-        var sourceKeys = source.animations[0].getKeys();
-
-        // rescaling prep
-        var sourceBoneLength = source.length;
-        var sourceParent = source.getParent();
-        var parent = this.getParent();
-        var parentScalingReqd = rescaleAsRequired && sourceParent && sourceBoneLength && this.length && sourceBoneLength !== this.length;
-        var parentRatio = parentScalingReqd && parent && sourceParent ? parent.length / sourceParent.length : 1;
-
-        var dimensionsScalingReqd = rescaleAsRequired && !parent && skelDimensionsRatio && (skelDimensionsRatio.x !== 1 || skelDimensionsRatio.y !== 1 || skelDimensionsRatio.z !== 1);
-
-        var destKeys = this.animations[0].getKeys();
-
-        // loop vars declaration
-        var orig: { frame: number, value: Matrix };
-        var origTranslation: Vector3;
-        var mat: Matrix;
-
-        for (var key = 0, nKeys = sourceKeys.length; key < nKeys; key++) {
-            orig = sourceKeys[key];
-            if (orig.frame >= from && orig.frame <= to) {
-                if (rescaleAsRequired) {
-                    mat = orig.value.clone();
-
-                    // scale based on parent ratio, when bone has parent
-                    if (parentScalingReqd) {
-                        origTranslation = mat.getTranslation();
-                        mat.setTranslation(origTranslation.scaleInPlace(parentRatio));
-
-                        // scale based on skeleton dimension ratio when root bone, and value is passed
-                    } else if (dimensionsScalingReqd && skelDimensionsRatio) {
-                        origTranslation = mat.getTranslation();
-                        mat.setTranslation(origTranslation.multiplyInPlace(skelDimensionsRatio));
-
-                        // use original when root bone, and no data for skelDimensionsRatio
-                    } else {
-                        mat = orig.value;
-                    }
-                } else {
-                    mat = orig.value;
-                }
-                destKeys.push({ frame: orig.frame + frameOffset, value: mat });
-            }
-        }
-        this.animations[0].createRange(rangeName, from + frameOffset, to + frameOffset);
-        return true;
-    }
-
-    /**
      * Translate the bone in local or world space
      * @param vec The amount to translate the bone
      * @param space The space that the translation is in

+ 2 - 1
src/Bones/skeleton.ts

@@ -9,7 +9,8 @@ import { AbstractMesh } from "../Meshes/abstractMesh";
 import { RawTexture } from "../Materials/Textures/rawTexture";
 import { Animatable } from "../Animations/animatable";
 import { AnimationPropertiesOverride } from "../Animations/animationPropertiesOverride";
-import { AnimationRange, Animation } from "../Animations/animation";
+import { Animation } from "../Animations/animation";
+import { AnimationRange } from "../Animations/animationRange";
 import { EngineStore } from "../Engines/engineStore";
 import { Constants } from "../Engines/constants";
 import { Logger } from "../Misc/logger";

+ 2 - 2
src/Cameras/Inputs/followCameraMouseWheelInput.ts

@@ -99,9 +99,9 @@ export class FollowCameraMouseWheelInput implements ICameraInput<FollowCamera> {
                 if (this.axisControlRadius) {
                     this.camera.radius += delta;
                 } else if (this.axisControlHeight) {
-                    this.camera.heightOffset += delta;
+                    this.camera.heightOffset -= delta;
                 } else if (this.axisControlRotation) {
-                    this.camera.rotationOffset += delta;
+                    this.camera.rotationOffset -= delta;
                 }
             }
 

+ 13 - 1
src/Cameras/VR/vrExperienceHelper.ts

@@ -26,6 +26,14 @@ import { DynamicTexture } from "../../Materials/Textures/dynamicTexture";
 import { ImageProcessingPostProcess } from "../../PostProcesses/imageProcessingPostProcess";
 import { SineEase, EasingFunction, CircleEase } from "../../Animations/easing";
 import { Animation } from "../../Animations/animation";
+import { VRCameraMetrics } from '../../Cameras/VR/vrCameraMetrics';
+
+import "../../Meshes/Builders/groundBuilder";
+import "../../Meshes/Builders/torusBuilder";
+import "../../Meshes/Builders/cylinderBuilder";
+
+import "../../Gamepads/gamepadSceneComponent";
+import "../../Animations/animatable";
 
 /**
  * Options to modify the vr teleportation behavior.
@@ -61,6 +69,10 @@ export interface VRExperienceHelperOptions extends WebVROptions {
      * A list of meshes to be used as the teleportation floor. If specified, teleportation will be enabled (default: undefined)
      */
     floorMeshes?: Mesh[];
+    /**
+     * Distortion metrics for the fallback vrDeviceOrientationCamera (default: VRCameraMetrics.Default)
+     */
+    vrDeviceOrientationCameraMetrics?: VRCameraMetrics;
 }
 
 class VRExperienceHelperGazer implements IDisposable {
@@ -674,7 +686,7 @@ export class VRExperienceHelper {
 
         // Create VR cameras
         if (webVROptions.createFallbackVRDeviceOrientationFreeCamera) {
-            this._vrDeviceOrientationCamera = new VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene);
+            this._vrDeviceOrientationCamera = new VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene, true, webVROptions.vrDeviceOrientationCameraMetrics);
             this._vrDeviceOrientationCamera.angularSensibility = Number.MAX_VALUE;
         }
         this._webVRCamera = new WebVRFreeCamera("WebVRHelper", this._position, this._scene, webVROptions);

+ 5 - 3
src/Cameras/arcRotateCamera.ts

@@ -9,7 +9,6 @@ import { Mesh } from "../Meshes/mesh";
 import { AutoRotationBehavior } from "../Behaviors/Cameras/autoRotationBehavior";
 import { BouncingBehavior } from "../Behaviors/Cameras/bouncingBehavior";
 import { FramingBehavior } from "../Behaviors/Cameras/framingBehavior";
-import { Collider } from "../Collisions/collider";
 import { Camera } from "./camera";
 import { TargetCamera } from "./targetCamera";
 import { ArcRotateCameraPointersInput } from "../Cameras/Inputs/arcRotateCameraPointersInput";
@@ -17,6 +16,8 @@ import { ArcRotateCameraKeyboardMoveInput } from "../Cameras/Inputs/arcRotateCam
 import { ArcRotateCameraMouseWheelInput } from "../Cameras/Inputs/arcRotateCameraMouseWheelInput";
 import { ArcRotateCameraInputsManager } from "../Cameras/arcRotateCameraInputsManager";
 
+declare type Collider = import("../Collisions/collider").Collider;
+
 Node.AddNodeConstructor("ArcRotateCamera", (name, scene) => {
     return () => new ArcRotateCamera(name, 0, 0, 1.0, Vector3.Zero(), scene);
 });
@@ -949,13 +950,14 @@ export class ArcRotateCamera extends TargetCamera {
 
         target.addToRef(this._computationVector, this._newPosition);
         if (this.getScene().collisionsEnabled && this.checkCollisions) {
+            const coordinator = this.getScene().collisionCoordinator;
             if (!this._collider) {
-                this._collider = new Collider();
+                this._collider = coordinator.createCollider();
             }
             this._collider._radius = this.collisionRadius;
             this._newPosition.subtractToRef(this.position, this._collisionVelocity);
             this._collisionTriggered = true;
-            this.getScene().collisionCoordinator.getNewPosition(this.position, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+            coordinator.getNewPosition(this.position, this._collisionVelocity, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
         } else {
             this.position.copyFrom(this._newPosition);
 

+ 9 - 19
src/Cameras/camera.ts

@@ -9,16 +9,16 @@ import { Matrix, Vector3, Viewport, Plane, Frustum } from "../Maths/math";
 import { Node } from "../node";
 import { Mesh } from "../Meshes/mesh";
 import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Ray } from "../Culling/ray";
 import { ICullable } from "../Culling/boundingInfo";
 import { Logger } from "../Misc/logger";
-
-import { Animation } from "../Animations/animation";
+import { _TypeStore } from '../Misc/typeStore';
+import { _DevTools } from '../Misc/devTools';
 
 declare type PostProcess = import("../PostProcesses/postProcess").PostProcess;
 declare type RenderTargetTexture = import("../Materials/Textures/renderTargetTexture").RenderTargetTexture;
 declare type FreeCamera = import("./freeCamera").FreeCamera;
 declare type TargetCamera = import("./targetCamera").TargetCamera;
+declare type Ray = import("../Culling/ray").Ray;
 
 /**
  * This is the base class of all the camera used in the application.
@@ -27,7 +27,7 @@ declare type TargetCamera = import("./targetCamera").TargetCamera;
 export class Camera extends Node {
     /** @hidden */
     public static _createDefaultParsedCamera = (name: string, scene: Scene): Camera => {
-        throw "UniversalCamera needs to be imported before deserialization can create a default camera.";
+        throw _DevTools.WarnImport("UniversalCamera");
     }
 
     /**
@@ -812,19 +812,7 @@ export class Camera extends Node {
      * @returns the forward ray
      */
     public getForwardRay(length = 100, transform?: Matrix, origin?: Vector3): Ray {
-        if (!transform) {
-            transform = this.getWorldMatrix();
-        }
-
-        if (!origin) {
-            origin = this.position;
-        }
-        var forward = this._scene.useRightHandedSystem ? new Vector3(0, 0, -1) : new Vector3(0, 0, 1);
-        var forwardWorld = Vector3.TransformNormal(forward, transform);
-
-        var direction = Vector3.Normalize(forwardWorld);
-
-        return new Ray(origin, direction, length);
+        throw _DevTools.WarnImport("Ray");
     }
 
     /**
@@ -1211,8 +1199,10 @@ export class Camera extends Node {
         if (parsedCamera.animations) {
             for (var animationIndex = 0; animationIndex < parsedCamera.animations.length; animationIndex++) {
                 var parsedAnimation = parsedCamera.animations[animationIndex];
-
-                camera.animations.push(Animation.Parse(parsedAnimation));
+                const internalClass = _TypeStore.GetClass("BABYLON.Animation");
+                if (internalClass) {
+                    camera.animations.push(internalClass.Parse(parsedAnimation));
+                }
             }
             Node.ParseAnimationRanges(camera, parsedCamera, scene);
         }

+ 6 - 3
src/Cameras/flyCamera.ts

@@ -4,11 +4,13 @@ import { Scene } from "../scene";
 import { Vector3, Quaternion } from "../Maths/math";
 import { Engine } from "../Engines/engine";
 import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Collider } from "../Collisions/collider";
 import { TargetCamera } from "./targetCamera";
 import { FlyCameraInputsManager } from "./flyCameraInputsManager";
 import { FlyCameraMouseInput } from "../Cameras/Inputs/flyCameraMouseInput";
 import { FlyCameraKeyboardInput } from "../Cameras/Inputs/flyCameraKeyboardInput";
+
+declare type Collider = import("../Collisions/collider").Collider;
+
 /**
  * This is a flying camera, designed for 3D movement and rotation in all directions,
  * such as in a 3D Space Shooter or a Flight Simulator.
@@ -318,9 +320,10 @@ export class FlyCamera extends TargetCamera {
 
         globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
         this._oldPosition.addInPlace(this.ellipsoidOffset);
+        let coordinator = this.getScene().collisionCoordinator;
 
         if (!this._collider) {
-            this._collider = new Collider();
+            this._collider = coordinator.createCollider();
         }
 
         this._collider._radius = this.ellipsoid;
@@ -335,7 +338,7 @@ export class FlyCamera extends TargetCamera {
             actualDisplacement = displacement.add(this.getScene().gravity);
         }
 
-        this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+        coordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
     }
 
     /** @hidden */

+ 5 - 3
src/Cameras/freeCamera.ts

@@ -2,7 +2,6 @@ import { Nullable } from "../types";
 import { serializeAsVector3, serialize } from "../Misc/decorators";
 import { Vector3, Vector2 } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Collider } from "../Collisions/collider";
 import { Scene } from "../scene";
 import { Engine } from "../Engines/engine";
 import { TargetCamera } from "./targetCamera";
@@ -10,6 +9,8 @@ import { FreeCameraInputsManager } from "./freeCameraInputsManager";
 import { FreeCameraMouseInput } from "../Cameras/Inputs/freeCameraMouseInput";
 import { FreeCameraKeyboardMoveInput } from "../Cameras/Inputs/freeCameraKeyboardMoveInput";
 
+declare type Collider = import("../Collisions/collider").Collider;
+
 /**
  * This represents a free type of camera. It can be useful in First Person Shooter game for instance.
  * Please consider using the new UniversalCamera instead as it adds more functionality like the gamepad.
@@ -229,8 +230,9 @@ export class FreeCamera extends TargetCamera {
         globalPosition.subtractFromFloatsToRef(0, this.ellipsoid.y, 0, this._oldPosition);
         this._oldPosition.addInPlace(this.ellipsoidOffset);
 
+        let coordinator = this.getScene().collisionCoordinator;
         if (!this._collider) {
-            this._collider = new Collider();
+            this._collider = coordinator.createCollider();
         }
 
         this._collider._radius = this.ellipsoid;
@@ -245,7 +247,7 @@ export class FreeCamera extends TargetCamera {
             actualDisplacement = displacement.add(this.getScene().gravity);
         }
 
-        this.getScene().collisionCoordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
+        coordinator.getNewPosition(this._oldPosition, actualDisplacement, this._collider, 3, null, this._onCollisionPositionChange, this.uniqueId);
 
     }
 

+ 10 - 23
src/Collisions/collisionCoordinator.ts

@@ -3,26 +3,17 @@ import { Scene } from "../scene";
 import { Vector3 } from "../Maths/math";
 import { Engine } from "../Engines/engine";
 import { Collider } from "./collider";
-import { Geometry } from "../Meshes/geometry";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 
 /** @hidden */
 export interface ICollisionCoordinator {
+    createCollider(): Collider;
     getNewPosition(position: Vector3, displacement: Vector3, collider: Collider, maximumRetry: number, excludedMesh: Nullable<AbstractMesh>, onNewPosition: (collisionIndex: number, newPosition: Vector3, collidedMesh: Nullable<AbstractMesh>) => void, collisionIndex: number): void;
     init(scene: Scene): void;
-    destroy(): void;
-
-    //Update meshes and geometries
-    onMeshAdded(mesh: AbstractMesh): void;
-    onMeshUpdated(mesh: AbstractMesh): void;
-    onMeshRemoved(mesh: AbstractMesh): void;
-    onGeometryAdded(geometry: Geometry): void;
-    onGeometryUpdated(geometry: Geometry): void;
-    onGeometryDeleted(geometry: Geometry): void;
 }
 
 /** @hidden */
-export class CollisionCoordinatorLegacy implements ICollisionCoordinator {
+export class DefaultCollisionCoordinator implements ICollisionCoordinator {
 
     private _scene: Scene;
 
@@ -45,22 +36,14 @@ export class CollisionCoordinatorLegacy implements ICollisionCoordinator {
         onNewPosition(collisionIndex, this._finalPosition, collider.collidedMesh);
     }
 
-    public init(scene: Scene): void {
-        this._scene = scene;
+    public createCollider(): Collider {
+        return new Collider();
     }
 
-    public destroy(): void {
-        //Legacy need no destruction method.
+    public init(scene: Scene): void {
+        this._scene = scene;
     }
 
-    //No update in legacy mode
-    public onMeshAdded(mesh: AbstractMesh) { }
-    public onMeshUpdated(mesh: AbstractMesh) { }
-    public onMeshRemoved(mesh: AbstractMesh) { }
-    public onGeometryAdded(geometry: Geometry) { }
-    public onGeometryUpdated(geometry: Geometry) { }
-    public onGeometryDeleted(geometry: Geometry) { }
-
     private _collideWithWorld(position: Vector3, velocity: Vector3, collider: Collider, maximumRetry: number, finalPosition: Vector3, excludedMesh: Nullable<AbstractMesh> = null): void {
         var closeDistance = Engine.CollisionsEpsilon * 10.0;
 
@@ -100,3 +83,7 @@ export class CollisionCoordinatorLegacy implements ICollisionCoordinator {
         this._collideWithWorld(position, velocity, collider, maximumRetry, finalPosition, excludedMesh);
     }
 }
+
+Scene.CollisionCoordinatorFactory = () => {
+    return new DefaultCollisionCoordinator();
+};

+ 5 - 1
src/Collisions/pickingInfo.ts

@@ -2,14 +2,18 @@ import { Nullable, FloatArray } from "../types";
 import { Vector3, Vector2, Tmp } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { VertexBuffer } from "../Meshes/buffer";
-import { Ray } from "../Culling/ray";
 import { Sprite } from "../Sprites/sprite";
 
+declare type Ray = import("../Culling/ray").Ray;
+
 /**
      * Information about the result of picking within a scene
      * @see https://doc.babylonjs.com/babylon101/picking_collisions
      */
 export class PickingInfo {
+    /** @hidden */
+    public _pickingUnavailable = false;
+
     /**
      * If the pick collided with an object
      */

+ 2 - 1
src/Culling/Octrees/octreeSceneComponent.ts

@@ -3,12 +3,13 @@ import { Scene } from "../../scene";
 import { Vector3 } from "../../Maths/math";
 import { SubMesh } from "../../Meshes/subMesh";
 import { AbstractMesh } from "../../Meshes/abstractMesh";
-import { Collider } from "../../Collisions/collider";
 import { Ray } from "../../Culling/ray";
 import { SceneComponentConstants } from "../../sceneComponent";
 
 import { Octree } from "./octree";
 
+declare type Collider = import("../../Collisions/collider").Collider;
+
 declare module "../../scene" {
     export interface Scene {
         /**

+ 2 - 2
src/Culling/boundingInfo.ts

@@ -2,11 +2,11 @@ import { DeepImmutable } from "../types";
 import { ArrayTools } from "../Misc/arrayTools";
 import { Matrix, Vector3, Plane } from "../Maths/math";
 import { Constants } from "../Engines/constants";
-import { Collider } from "../Collisions/collider";
-
 import { BoundingBox } from "./boundingBox";
 import { BoundingSphere } from "./boundingSphere";
 
+declare type Collider = import("../Collisions/collider").Collider;
+
 const _result0 = { min: 0, max: 0 };
 const _result1 = { min: 0, max: 0 };
 const computeBoxExtents = (axis: DeepImmutable<Vector3>, box: DeepImmutable<BoundingBox>, result: { min: number, max: number }) => {

+ 233 - 0
src/Culling/ray.ts

@@ -6,6 +6,8 @@ import { PickingInfo } from "../Collisions/pickingInfo";
 import { IntersectionInfo } from "../Collisions/intersectionInfo";
 import { BoundingBox } from "./boundingBox";
 import { BoundingSphere } from "./boundingSphere";
+import { Scene } from '../scene';
+import { Camera } from '../Cameras/camera';
 /**
  * Class representing a ray with position and direction
  */
@@ -530,3 +532,234 @@ export class Ray {
         this.direction.normalize();
     }
 }
+
+// Picking
+
+declare module "../scene" {
+    export interface Scene {
+        /** @hidden */
+        _tempPickingRay: Nullable<Ray>;
+
+        /** @hidden */
+        _cachedRayForTransform: Ray;
+
+        /** @hidden */
+        _pickWithRayInverseMatrix: Matrix;
+
+        /** @hidden */
+        _internalPick(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean): Nullable<PickingInfo>;
+
+        /** @hidden */
+        _internalMultiPick(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean): Nullable<PickingInfo[]>;
+
+    }
+}
+
+Scene.prototype.createPickingRay = function(x: number, y: number, world: Matrix, camera: Nullable<Camera>, cameraViewSpace = false): Ray {
+    let result = Ray.Zero();
+
+    this.createPickingRayToRef(x, y, world, result, camera, cameraViewSpace);
+
+    return result;
+};
+
+Scene.prototype.createPickingRayToRef = function(x: number, y: number, world: Matrix, result: Ray, camera: Nullable<Camera>, cameraViewSpace = false): Scene {
+    var engine = this.getEngine();
+
+    if (!camera) {
+        if (!this.activeCamera) {
+            throw new Error("Active camera not set");
+        }
+
+        camera = this.activeCamera;
+    }
+
+    var cameraViewport = camera.viewport;
+    var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+
+    // Moving coordinates to local viewport world
+    x = x / engine.getHardwareScalingLevel() - viewport.x;
+    y = y / engine.getHardwareScalingLevel() - (engine.getRenderHeight() - viewport.y - viewport.height);
+
+    result.update(x, y, viewport.width, viewport.height, world ? world : Matrix.IdentityReadOnly, cameraViewSpace ? Matrix.IdentityReadOnly : camera.getViewMatrix(), camera.getProjectionMatrix());
+    return this;
+};
+
+Scene.prototype.createPickingRayInCameraSpace = function(x: number, y: number, camera?: Camera): Ray {
+    let result = Ray.Zero();
+
+    this.createPickingRayInCameraSpaceToRef(x, y, result, camera);
+
+    return result;
+};
+
+Scene.prototype.createPickingRayInCameraSpaceToRef = function(x: number, y: number, result: Ray, camera?: Camera): Scene {
+    if (!PickingInfo) {
+        return this;
+    }
+
+    var engine = this.getEngine();
+
+    if (!camera) {
+        if (!this.activeCamera) {
+            throw new Error("Active camera not set");
+        }
+
+        camera = this.activeCamera;
+    }
+
+    var cameraViewport = camera.viewport;
+    var viewport = cameraViewport.toGlobal(engine.getRenderWidth(), engine.getRenderHeight());
+    var identity = Matrix.Identity();
+
+    // Moving coordinates to local viewport world
+    x = x / engine.getHardwareScalingLevel() - viewport.x;
+    y = y / engine.getHardwareScalingLevel() - (engine.getRenderHeight() - viewport.y - viewport.height);
+    result.update(x, y, viewport.width, viewport.height, identity, identity, camera.getProjectionMatrix());
+    return this;
+};
+
+Scene.prototype._internalPick = function(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean): Nullable<PickingInfo> {
+    if (!PickingInfo) {
+        return null;
+    }
+
+    var pickingInfo = null;
+
+    for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
+        var mesh = this.meshes[meshIndex];
+
+        if (predicate) {
+            if (!predicate(mesh)) {
+                continue;
+            }
+        } else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
+            continue;
+        }
+
+        var world = mesh.getWorldMatrix();
+        var ray = rayFunction(world);
+
+        var result = mesh.intersects(ray, fastCheck);
+        if (!result || !result.hit) {
+            continue;
+        }
+
+        if (!fastCheck && pickingInfo != null && result.distance >= pickingInfo.distance) {
+            continue;
+        }
+
+        pickingInfo = result;
+
+        if (fastCheck) {
+            break;
+        }
+    }
+
+    return pickingInfo || new PickingInfo();
+};
+
+Scene.prototype._internalMultiPick = function(rayFunction: (world: Matrix) => Ray, predicate?: (mesh: AbstractMesh) => boolean): Nullable<PickingInfo[]> {
+    if (!PickingInfo) {
+        return null;
+    }
+    var pickingInfos = new Array<PickingInfo>();
+
+    for (var meshIndex = 0; meshIndex < this.meshes.length; meshIndex++) {
+        var mesh = this.meshes[meshIndex];
+
+        if (predicate) {
+            if (!predicate(mesh)) {
+                continue;
+            }
+        } else if (!mesh.isEnabled() || !mesh.isVisible || !mesh.isPickable) {
+            continue;
+        }
+
+        var world = mesh.getWorldMatrix();
+        var ray = rayFunction(world);
+
+        var result = mesh.intersects(ray, false);
+        if (!result || !result.hit) {
+            continue;
+        }
+
+        pickingInfos.push(result);
+    }
+
+    return pickingInfos;
+};
+
+Scene.prototype.pick = function(x: number, y: number, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean, camera?: Nullable<Camera>): Nullable<PickingInfo> {
+    if (!PickingInfo) {
+        return null;
+    }
+    var result = this._internalPick((world) => {
+        if (!this._tempPickingRay) {
+            this._tempPickingRay = Ray.Zero();
+        }
+
+        this.createPickingRayToRef(x, y, world, this._tempPickingRay, camera || null);
+        return this._tempPickingRay;
+    }, predicate, fastCheck);
+    if (result) {
+        result.ray = this.createPickingRay(x, y, Matrix.Identity(), camera || null);
+    }
+    return result;
+};
+
+Scene.prototype.pickWithRay = function(ray: Ray, predicate?: (mesh: AbstractMesh) => boolean, fastCheck?: boolean): Nullable<PickingInfo> {
+    var result = this._internalPick((world) => {
+        if (!this._pickWithRayInverseMatrix) {
+            this._pickWithRayInverseMatrix = Matrix.Identity();
+        }
+        world.invertToRef(this._pickWithRayInverseMatrix);
+
+        if (!this._cachedRayForTransform) {
+            this._cachedRayForTransform = Ray.Zero();
+        }
+
+        Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
+        return this._cachedRayForTransform;
+    }, predicate, fastCheck);
+    if (result) {
+        result.ray = ray;
+    }
+    return result;
+};
+
+Scene.prototype.multiPick = function(x: number, y: number, predicate?: (mesh: AbstractMesh) => boolean, camera?: Camera): Nullable<PickingInfo[]> {
+    return this._internalMultiPick((world) => this.createPickingRay(x, y, world, camera || null), predicate);
+};
+
+Scene.prototype.multiPickWithRay = function(ray: Ray, predicate: (mesh: AbstractMesh) => boolean): Nullable<PickingInfo[]> {
+    return this._internalMultiPick((world) => {
+        if (!this._pickWithRayInverseMatrix) {
+            this._pickWithRayInverseMatrix = Matrix.Identity();
+        }
+        world.invertToRef(this._pickWithRayInverseMatrix);
+
+        if (!this._cachedRayForTransform) {
+            this._cachedRayForTransform = Ray.Zero();
+        }
+
+        Ray.TransformToRef(ray, this._pickWithRayInverseMatrix, this._cachedRayForTransform);
+        return this._cachedRayForTransform;
+    }, predicate);
+};
+
+Camera.prototype.getForwardRay = function(length = 100, transform?: Matrix, origin?: Vector3): Ray {
+    if (!transform) {
+        transform = this.getWorldMatrix();
+    }
+
+    if (!origin) {
+        origin = this.position;
+    }
+    var forward = this._scene.useRightHandedSystem ? new Vector3(0, 0, -1) : new Vector3(0, 0, 1);
+    var forwardWorld = Vector3.TransformNormal(forward, transform);
+
+    var direction = Vector3.Normalize(forwardWorld);
+
+    return new Ray(origin, direction, length);
+};

+ 4 - 3
src/Debug/physicsViewer.ts

@@ -2,7 +2,8 @@ import { Nullable } from "../types";
 import { Scene } from "../scene";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
-import { MeshBuilder } from "../Meshes/meshBuilder";
+import { BoxBuilder } from "../Meshes/Builders/boxBuilder";
+import { SphereBuilder } from "../Meshes/Builders/sphereBuilder";
 import { Quaternion, Color3 } from "../Maths/math";
 import { Material } from "../Materials/material";
 import { EngineStore } from "../Engines/engineStore";
@@ -164,7 +165,7 @@ export class PhysicsViewer {
 
     private _getDebugBoxMesh(scene: Scene): AbstractMesh {
         if (!this._debugBoxMesh) {
-            this._debugBoxMesh = MeshBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
+            this._debugBoxMesh = BoxBuilder.CreateBox('physicsBodyBoxViewMesh', { size: 1 }, scene);
             this._debugBoxMesh.rotationQuaternion = Quaternion.Identity();
             this._debugBoxMesh.material = this._getDebugMaterial(scene);
         }
@@ -174,7 +175,7 @@ export class PhysicsViewer {
 
     private _getDebugSphereMesh(scene: Scene): AbstractMesh {
         if (!this._debugSphereMesh) {
-            this._debugSphereMesh = MeshBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
+            this._debugSphereMesh = SphereBuilder.CreateSphere('physicsBodySphereViewMesh', { diameter: 1 }, scene);
             this._debugSphereMesh.rotationQuaternion = Quaternion.Identity();
             this._debugSphereMesh.material = this._getDebugMaterial(scene);
         }

+ 2 - 0
src/Debug/rayHelper.ts

@@ -6,6 +6,8 @@ import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
 import { LinesMesh } from "../Meshes/linesMesh";
 
+import "../Meshes/Builders/linesBuilder";
+
 /**
  * As raycast might be hard to debug, the RayHelper can help rendering the different rays
  * in order to better appreciate the issue one might have.

+ 3 - 3
src/Debug/skeletonViewer.ts

@@ -5,7 +5,7 @@ import { Bone } from "../Bones/bone";
 import { Skeleton } from "../Bones/skeleton";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { LinesMesh } from "../Meshes/linesMesh";
-import { MeshBuilder } from "../Meshes/meshBuilder";
+import { LinesBuilder } from "../Meshes/Builders/linesBuilder";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 
 /**
@@ -161,10 +161,10 @@ export class SkeletonViewer {
         const targetScene = this._utilityLayer.utilityLayerScene;
 
         if (!this._debugMesh) {
-            this._debugMesh = MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
+            this._debugMesh = LinesBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
             this._debugMesh.renderingGroupId = this.renderingGroupId;
         } else {
-            MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: this._debugMesh }, targetScene);
+            LinesBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: this._debugMesh }, targetScene);
         }
         this._debugMesh.position.copyFrom(this.mesh.position);
         this._debugMesh.color = this.color;

+ 3 - 2
src/Engines/engine.ts

@@ -28,6 +28,7 @@ import { DomManagement } from "../Misc/domManagement";
 import { Logger } from "../Misc/logger";
 import { EngineStore } from "./engineStore";
 import { RenderTargetCreationOptions } from "../Materials/Textures/renderTargetCreationOptions";
+import { _DevTools } from '../Misc/devTools';
 
 declare type PostProcess = import("../PostProcesses/postProcess").PostProcess;
 declare type Texture = import("../Materials/Textures/texture").Texture;
@@ -490,7 +491,7 @@ export class Engine {
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.0.0-alpha.19";
+        return "4.0.0-alpha.21";
     }
 
     /**
@@ -530,7 +531,7 @@ export class Engine {
      * @returns The loading screen
      */
     public static DefaultLoadingScreenFactory(canvas: HTMLCanvasElement): ILoadingScreen {
-        throw "Import LoadingScreen or set DefaultLoadingScreenFactory on engine before using the loading screen";
+        throw _DevTools.WarnImport("LoadingScreen");
     }
 
     /**

+ 3 - 1
src/Events/pointerEvents.ts

@@ -1,9 +1,11 @@
 import { Nullable } from "../types";
 import { Vector2 } from "../Maths/math";
 import { PickingInfo } from "../Collisions/pickingInfo";
-import { Ray } from "../Culling/ray";
 import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
+
+declare type Ray = import("../Culling/ray").Ray;
+
 /**
  * Gather the list of pointer event types as constants.
  */

+ 4 - 3
src/Gizmos/axisDragGizmo.ts

@@ -6,7 +6,8 @@ import { TransformNode } from "../Meshes/transformNode";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
 import { LinesMesh } from "../Meshes/linesMesh";
-import { MeshBuilder } from "../Meshes/meshBuilder";
+import { CylinderBuilder } from "../Meshes/Builders/cylinderBuilder";
+import { LinesBuilder } from "../Meshes/Builders/linesBuilder";
 import { PointerDragBehavior } from "../Behaviors/Meshes/pointerDragBehavior";
 import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
@@ -36,8 +37,8 @@ export class AxisDragGizmo extends Gizmo {
     /** @hidden */
     public static _CreateArrow(scene: Scene, material: StandardMaterial): TransformNode {
         var arrow = new TransformNode("arrow", scene);
-        var cylinder = MeshBuilder.CreateCylinder("cylinder", { diameterTop: 0, height: 1.5, diameterBottom: 0.75, tessellation: 96 }, scene);
-        var line = MeshBuilder.CreateLines("line", { points: [new Vector3(0, 0, 0), new Vector3(0, 1.1, 0)] }, scene);
+        var cylinder = CylinderBuilder.CreateCylinder("cylinder", { diameterTop: 0, height: 1.5, diameterBottom: 0.75, tessellation: 96 }, scene);
+        var line = LinesBuilder.CreateLines("line", { points: [new Vector3(0, 0, 0), new Vector3(0, 1.1, 0)] }, scene);
         line.color = material.emissiveColor;
         cylinder.parent = arrow;
         line.parent = arrow;

+ 4 - 3
src/Gizmos/axisScaleGizmo.ts

@@ -5,7 +5,8 @@ import { Vector3, Color3 } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
 import { LinesMesh } from "../Meshes/linesMesh";
-import { MeshBuilder } from "../Meshes/meshBuilder";
+import { BoxBuilder } from "../Meshes/Builders/boxBuilder";
+import { LinesBuilder } from "../Meshes/Builders/linesBuilder";
 import { StandardMaterial } from "../Materials/standardMaterial";
 import { PointerDragBehavior } from "../Behaviors/Meshes/pointerDragBehavior";
 import { _TimeToken } from "../Instrumentation/timeToken";
@@ -55,8 +56,8 @@ export class AxisScaleGizmo extends Gizmo {
 
         // Build mesh on root node
         var arrow = new AbstractMesh("", gizmoLayer.utilityLayerScene);
-        var arrowMesh = MeshBuilder.CreateBox("yPosMesh", { size: 0.4 }, gizmoLayer.utilityLayerScene);
-        var arrowTail = MeshBuilder.CreateLines("yPosMesh", { points: [new Vector3(0, 0, 0), new Vector3(0, 1.1, 0)] }, gizmoLayer.utilityLayerScene);
+        var arrowMesh = BoxBuilder.CreateBox("yPosMesh", { size: 0.4 }, gizmoLayer.utilityLayerScene);
+        var arrowTail = LinesBuilder.CreateLines("yPosMesh", { points: [new Vector3(0, 0, 0), new Vector3(0, 1.1, 0)] }, gizmoLayer.utilityLayerScene);
         arrowTail.color = this._coloredMaterial.emissiveColor;
         arrow.addChild(arrowMesh);
         arrow.addChild(arrowTail);

+ 21 - 16
src/Gizmos/boundingBoxGizmo.ts

@@ -6,7 +6,9 @@ import { Scene } from "../scene";
 import { Quaternion, Matrix, Vector3, Color3, Epsilon } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
-import { MeshBuilder } from "../Meshes/meshBuilder";
+import { SphereBuilder } from "../Meshes/Builders/sphereBuilder";
+import { BoxBuilder } from "../Meshes/Builders/boxBuilder";
+import { LinesBuilder } from "../Meshes/Builders/linesBuilder";
 import { PointerDragBehavior } from "../Behaviors/Meshes/pointerDragBehavior";
 import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
@@ -14,6 +16,9 @@ import { Gizmo } from "./gizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 import { StandardMaterial } from "../Materials/standardMaterial";
 import { PivotTools } from "../Misc/pivotTools";
+
+import "../Meshes/Builders/boxBuilder";
+
 /**
  * Bounding box gizmo
  */
@@ -110,18 +115,18 @@ export class BoundingBoxGizmo extends Gizmo {
         this._lineBoundingBox = new AbstractMesh("", gizmoLayer.utilityLayerScene);
         this._lineBoundingBox.rotationQuaternion = new Quaternion();
         var lines = [];
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, 0, 0), new Vector3(this._boundingDimensions.x, 0, 0)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, 0, 0), new Vector3(0, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, 0, 0), new Vector3(0, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, 0, 0), new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, 0, 0), new Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, this._boundingDimensions.y, 0), new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, this._boundingDimensions.y, 0), new Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, 0, this._boundingDimensions.z), new Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(0, 0, this._boundingDimensions.z), new Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
-        lines.push(MeshBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, 0, 0), new Vector3(this._boundingDimensions.x, 0, 0)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, 0, 0), new Vector3(0, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, 0, 0), new Vector3(0, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, 0, 0), new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, 0, 0), new Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, this._boundingDimensions.y, 0), new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, this._boundingDimensions.y, 0), new Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, 0, this._boundingDimensions.z), new Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(0, 0, this._boundingDimensions.z), new Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new Vector3(0, this._boundingDimensions.y, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new Vector3(this._boundingDimensions.x, 0, this._boundingDimensions.z)] }, gizmoLayer.utilityLayerScene));
+        lines.push(LinesBuilder.CreateLines("lines", { points: [new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, this._boundingDimensions.z), new Vector3(this._boundingDimensions.x, this._boundingDimensions.y, 0)] }, gizmoLayer.utilityLayerScene));
         lines.forEach((l) => {
             l.color = color;
             l.position.addInPlace(new Vector3(-this._boundingDimensions.x / 2, -this._boundingDimensions.y / 2, -this._boundingDimensions.z / 2));
@@ -134,7 +139,7 @@ export class BoundingBoxGizmo extends Gizmo {
         this._rotateSpheresParent = new AbstractMesh("", gizmoLayer.utilityLayerScene);
         this._rotateSpheresParent.rotationQuaternion = new Quaternion();
         for (let i = 0; i < 12; i++) {
-            let sphere = MeshBuilder.CreateSphere("", { diameter: 1 }, gizmoLayer.utilityLayerScene);
+            let sphere = SphereBuilder.CreateSphere("", { diameter: 1 }, gizmoLayer.utilityLayerScene);
             sphere.rotationQuaternion = new Quaternion();
             sphere.material = coloredMaterial;
 
@@ -224,7 +229,7 @@ export class BoundingBoxGizmo extends Gizmo {
         for (var i = 0; i < 2; i++) {
             for (var j = 0; j < 2; j++) {
                 for (var k = 0; k < 2; k++) {
-                    let box = MeshBuilder.CreateBox("", { size: 1 }, gizmoLayer.utilityLayerScene);
+                    let box = BoxBuilder.CreateBox("", { size: 1 }, gizmoLayer.utilityLayerScene);
                     box.material = coloredMaterial;
 
                     // Dragging logic
@@ -544,7 +549,7 @@ export class BoundingBoxGizmo extends Gizmo {
         mesh.position.set(0, 0, 0);
 
         // Update bounding dimensions/positions
-        var box = MeshBuilder.CreateBox("box", { size: 1 }, mesh.getScene());
+        var box = BoxBuilder.CreateBox("box", { size: 1 }, mesh.getScene());
         var boundingMinMax = mesh.getHierarchyBoundingVectors();
         boundingMinMax.max.subtractToRef(boundingMinMax.min, box.scaling);
 

+ 3 - 0
src/Gizmos/planeRotationGizmo.ts

@@ -11,6 +11,9 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index"
 import { Gizmo } from "./gizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
 import { StandardMaterial } from "../Materials/standardMaterial";
+
+import "../Meshes/Builders/linesBuilder";
+
 /**
  * Single plane rotation gizmo
  */

+ 3 - 3
src/Gizmos/scaleGizmo.ts

@@ -3,7 +3,7 @@ import { Observable } from "../Misc/observable";
 import { Nullable } from "../types";
 import { Vector3, Color3 } from "../Maths/math";
 import { AbstractMesh } from "../Meshes/abstractMesh";
-import { Mesh } from "../Meshes/mesh";
+import { PolyhedronBuilder } from "../Meshes/Builders/polyhedronBuilder";
 import { Gizmo } from "./gizmo";
 import { AxisScaleGizmo } from "./axisScaleGizmo";
 import { UtilityLayerRenderer } from "../Rendering/utilityLayerRenderer";
@@ -56,10 +56,10 @@ export class ScaleGizmo extends Gizmo {
         this.uniformScaleGizmo = new AxisScaleGizmo(new Vector3(0, 1, 0), Color3.Yellow().scale(0.5), gizmoLayer);
         this.uniformScaleGizmo.updateGizmoRotationToMatchAttachedMesh = false;
         this.uniformScaleGizmo.uniformScaling = true;
-        var uniformScalingMesh = Mesh.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
+        var uniformScalingMesh = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
         uniformScalingMesh.scaling.scaleInPlace(0.02);
         uniformScalingMesh.visibility = 0;
-        var octahedron = Mesh.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
+        var octahedron = PolyhedronBuilder.CreatePolyhedron("", { type: 1 }, this.uniformScaleGizmo.gizmoLayer.utilityLayerScene);
         octahedron.scaling.scaleInPlace(0.007);
         uniformScalingMesh.addChild(octahedron);
         this.uniformScaleGizmo.setCustomMesh(uniformScalingMesh, true);

+ 4 - 0
src/Helpers/environmentHelper.ts

@@ -13,6 +13,10 @@ import { BackgroundMaterial } from "../Materials/Background/backgroundMaterial";
 import { _TimeToken } from "../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../States/index";
 import { Constants } from "../Engines/constants";
+
+import "../Meshes/Builders/planeBuilder";
+import "../Meshes/Builders/boxBuilder";
+
 /**
  * Represents the different options available during the creation of
  * a Environment helper.

+ 0 - 0
src/Helpers/photoDome.ts


Неке датотеке нису приказане због велике количине промена