Pārlūkot izejas kodu

Merge remote-tracking branch 'BabylonJS/master' into gui-module

# Conflicts:
#	dist/preview release/gui/babylon.gui.min.js
#	gui/src/2D/controls/control.ts
#	src/Gizmos/babylon.boundingBoxGizmo.ts
Raanan Weber 7 gadi atpakaļ
vecāks
revīzija
4c3e89bd76
56 mainītis faili ar 30221 papildinājumiem un 27690 dzēšanām
  1. 8467 8328
      Playground/babylon.d.txt
  2. 5 1
      Playground/js/index.js
  3. BIN
      Playground/textures/hollow.png
  4. 54 9
      Tools/Gulp/config.json
  5. BIN
      Viewer/dist/assets/environment/EnvMap_3.0-256.env
  6. 9851 9719
      dist/preview release/babylon.d.ts
  7. 54 54
      dist/preview release/babylon.js
  8. 2707 2269
      dist/preview release/babylon.max.js
  9. 2707 2269
      dist/preview release/babylon.no-module.max.js
  10. 54 54
      dist/preview release/babylon.worker.js
  11. 2709 2271
      dist/preview release/es6.js
  12. 1 0
      dist/preview release/gui/babylon.gui.js
  13. 5 0
      dist/preview release/gui/babylon.gui.min.js
  14. 1 0
      dist/preview release/materialsLibrary/babylon.gradientMaterial.d.ts
  15. 7 2
      dist/preview release/materialsLibrary/babylon.gradientMaterial.js
  16. 1 1
      dist/preview release/materialsLibrary/babylon.gradientMaterial.min.js
  17. 1 0
      dist/preview release/materialsLibrary/babylonjs.materials.d.ts
  18. 7 2
      dist/preview release/materialsLibrary/babylonjs.materials.js
  19. 2 2
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  20. 1 0
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  21. 0 1
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  22. 7 31
      dist/preview release/serializers/babylon.glTF2Serializer.js
  23. 2 2
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  24. 0 1
      dist/preview release/serializers/babylonjs.serializers.d.ts
  25. 7 31
      dist/preview release/serializers/babylonjs.serializers.js
  26. 2 2
      dist/preview release/serializers/babylonjs.serializers.min.js
  27. 0 1
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  28. 61 61
      dist/preview release/viewer/babylon.viewer.js
  29. 2707 2269
      dist/preview release/viewer/babylon.viewer.max.js
  30. 1 0
      dist/preview release/what's new.md
  31. 1 0
      gui/src/2D/controls/control.ts
  32. 5 1
      materialsLibrary/src/gradient/babylon.gradientMaterial.ts
  33. 2 1
      materialsLibrary/src/gradient/gradient.fragment.fx
  34. 7 34
      serializers/src/glTF/2.0/babylon.glTFExporter.ts
  35. 84 187
      src/Engine/babylon.engine.ts
  36. 3 0
      src/Gizmos/babylon.axisDragGizmo.ts
  37. 4 1
      src/Gizmos/babylon.axisScaleGizmo.ts
  38. 8 1
      src/Gizmos/babylon.boundingBoxGizmo.ts
  39. 20 0
      src/Gizmos/babylon.gizmo.ts
  40. 4 1
      src/Gizmos/babylon.planeRotationGizmo.ts
  41. 8 0
      src/Gizmos/babylon.positionGizmo.ts
  42. 8 0
      src/Gizmos/babylon.rotationGizmo.ts
  43. 123 0
      src/Materials/Textures/Loaders/babylon.ddsTextureLoader.ts
  44. 90 0
      src/Materials/Textures/Loaders/babylon.envTextureLoader.ts
  45. 97 0
      src/Materials/Textures/Loaders/babylon.ktxTextureLoader.ts
  46. 75 0
      src/Materials/Textures/Loaders/babylon.tgaTextureLoader.ts
  47. 6 14
      src/Materials/Textures/Procedurals/babylon.noiseProceduralTexture.ts
  48. 57 0
      src/Materials/Textures/babylon.internalTextureLoader.ts
  49. 8 9
      src/Particles/EmitterTypes/babylon.sphereParticleEmitter.ts
  50. 8 0
      src/Particles/babylon.IParticleSystem.ts
  51. 27 5
      src/Particles/babylon.gpuParticleSystem.ts
  52. 87 17
      src/Particles/babylon.particleSystem.ts
  53. 17 1
      src/Shaders/gpuUpdateParticles.vertex.fx
  54. 23 37
      src/Shaders/noise.fragment.fx
  55. 8 1
      src/Tools/babylon.sceneSerializer.ts
  56. 20 0
      src/Tools/babylon.tools.ts

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 8467 - 8328
Playground/babylon.d.txt


+ 5 - 1
Playground/js/index.js

@@ -179,7 +179,11 @@ function showError(errorMessage, errorEvent) {
         var checkTypescriptSupport = function(xhr) {
             var filename = location.pathname.substring(location.pathname.lastIndexOf('/') + 1);
             if (xhr.responseText.indexOf("class Playground") !== -1) {// Typescript content
-                if (filename !== "ts.html") {
+                if(!filename) {
+                    window.location.href = location.protocol + "//" + location.host + "/ts.html" + window.location.hash;
+                    return false;
+                }
+                else if (filename !== "ts.html") {
                     window.location.href = location.protocol + "//" + location.host + location.pathname.replace(filename, "ts.html") + window.location.hash;
                     return false;
                 }

BIN
Playground/textures/hollow.png


+ 54 - 9
Tools/Gulp/config.json

@@ -176,6 +176,7 @@
                 "../../src/babylon.assetContainer.js",
                 "../../src/Mesh/babylon.buffer.js",
                 "../../src/Mesh/babylon.vertexBuffer.js",
+                "../../src/Materials/Textures/babylon.internalTextureLoader.js",
                 "../../src/Materials/Textures/babylon.internalTextureTracker.js",
                 "../../src/Materials/Textures/babylon.internalTexture.js",
                 "../../src/Materials/Textures/babylon.baseTexture.js",
@@ -480,7 +481,8 @@
                 "../../src/Materials/PBR/babylon.pbrSpecularGlossinessMaterial.js"
             ],
             "dependUpon": [
-                "core"
+                "core",
+                "harmonics"
             ],
             "shaders": [
                 "pbr.vertex",
@@ -1018,15 +1020,13 @@
         },
         "hdr": {
             "files": [
-                "../../src/Math/babylon.sphericalPolynomial.js",
-                "../../src/Tools/HDR/babylon.cubemapToSphericalPolynomial.js",
-                "../../src/Tools/HDR/babylon.panoramaToCubemap.js",
                 "../../src/Tools/HDR/babylon.hdr.js",
-                "../../src/Tools/babylon.environmentTextureTools.js",
-                "../../src/Materials/Textures/babylon.hdrCubeTexture.js"
+                "../../src/Materials/Textures/babylon.hdrCubeTexture.js",
+                "../../src/Tools/HDR/babylon.panoramaToCubemap.js"
             ],
             "dependUpon": [
-                "core"
+                "core",
+                "harmonics"
             ]
         },
         "csg": {
@@ -1073,16 +1073,61 @@
                 "picking"
             ]
         },
+        "harmonics": {
+            "files": [
+                "../../src/Math/babylon.sphericalPolynomial.js",
+                "../../src/Tools/HDR/babylon.cubemapToSphericalPolynomial.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
         "textureFormats": {
+            "files": [ ],
+            "dependUpon": [
+                "dds",
+                "tga",
+                "ktx",
+                "env"
+            ]
+        },
+        "dds": {
             "files": [
-                "../../src/Tools/babylon.tga.js",
                 "../../src/Tools/babylon.dds.js",
-                "../../src/Tools/babylon.khronosTextureContainer.js"
+                "../../src/Materials/Textures/Loaders/babylon.ddsTextureLoader.js"
             ],
             "dependUpon": [
                 "core"
             ]
         },
+        "tga": {
+            "files": [
+                "../../src/Tools/babylon.tga.js",
+                "../../src/Materials/Textures/Loaders/babylon.tgaTextureLoader.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
+        "ktx": {
+            "files": [
+                "../../src/Tools/babylon.khronosTextureContainer.js",
+                "../../src/Materials/Textures/Loaders/babylon.ktxTextureLoader.js"
+            ],
+            "dependUpon": [
+                "core"
+            ]
+        },
+        "env": {
+            "files": [
+                "../../src/Tools/babylon.environmentTextureTools.js",
+                "../../src/Materials/Textures/Loaders/babylon.envTextureLoader.js"
+            ],
+            "dependUpon": [
+                "core",
+                "harmonics"
+            ]
+        },
         "debug": {
             "files": [
                 "../../src/Debug/babylon.skeletonViewer.js",

BIN
Viewer/dist/assets/environment/EnvMap_3.0-256.env


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 9851 - 9719
dist/preview release/babylon.d.ts


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 54 - 54
dist/preview release/babylon.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2707 - 2269
dist/preview release/babylon.max.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2707 - 2269
dist/preview release/babylon.no-module.max.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 54 - 54
dist/preview release/babylon.worker.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2709 - 2271
dist/preview release/es6.js


+ 1 - 0
dist/preview release/gui/babylon.gui.js

@@ -2149,6 +2149,7 @@ var BABYLON;
             /** @hidden */
             Control.prototype._resetFontCache = function () {
                 this._fontSet = true;
+                this._markAsDirty();
             };
             /**
              * Gets coordinates in local control space

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 5 - 0
dist/preview release/gui/babylon.gui.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylon.gradientMaterial.d.ts

@@ -8,6 +8,7 @@ declare module BABYLON {
         bottomColor: Color3;
         bottomColorAlpha: number;
         offset: number;
+        scale: number;
         smoothness: number;
         disableLighting: boolean;
         private _scaledDiffuse;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 7 - 2
dist/preview release/materialsLibrary/babylon.gradientMaterial.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 1 - 1
dist/preview release/materialsLibrary/babylon.gradientMaterial.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.d.ts

@@ -27,6 +27,7 @@ declare module BABYLON {
         bottomColor: Color3;
         bottomColorAlpha: number;
         offset: number;
+        scale: number;
         smoothness: number;
         disableLighting: boolean;
         private _scaledDiffuse;

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 7 - 2
dist/preview release/materialsLibrary/babylonjs.materials.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 2
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -32,6 +32,7 @@ declare module BABYLON {
         bottomColor: Color3;
         bottomColorAlpha: number;
         offset: number;
+        scale: number;
         smoothness: number;
         disableLighting: boolean;
         private _scaledDiffuse;

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

@@ -287,7 +287,6 @@ declare module BABYLON.GLTF2 {
          * @param binaryWriter Buffer to write binary data to
          */
         private createSceneAsync(babylonScene, binaryWriter);
-        private getRootNodes(babylonScene, nodes, shouldExportTransformNode);
         /**
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene

+ 7 - 31
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -874,7 +874,7 @@ var BABYLON;
                         for (var _a = 0, _b = bufferMesh.subMeshes; _a < _b.length; _a++) {
                             var submesh = _b[_a];
                             uvCoordsPresent = false;
-                            var babylonMaterial = submesh.getMaterial();
+                            var babylonMaterial = submesh.getMaterial() || bufferMesh.getScene().defaultMaterial;
                             var materialIndex = null;
                             if (babylonMaterial) {
                                 if (bufferMesh instanceof BABYLON.LinesMesh) {
@@ -891,8 +891,9 @@ var BABYLON;
                                     materialIndex = this._materials.length - 1;
                                 }
                                 else if (babylonMaterial instanceof BABYLON.MultiMaterial) {
-                                    babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
-                                    if (babylonMaterial) {
+                                    var subMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
+                                    if (subMaterial) {
+                                        babylonMaterial = subMaterial;
                                         materialIndex = this._materialMap[babylonMaterial.uniqueId];
                                     }
                                 }
@@ -939,7 +940,7 @@ var BABYLON;
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
                             if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
-                                var sideOrientation = this._babylonScene.materials[materialIndex].sideOrientation;
+                                var sideOrientation = babylonMaterial.sideOrientation;
                                 if (this._convertToRightHandedSystem && sideOrientation === BABYLON.Material.ClockWiseSideOrientation) {
                                     //Overwrite the indices to be counter-clockwise
                                     var byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
@@ -1037,18 +1038,6 @@ var BABYLON;
                     }
                 });
             };
-            _Exporter.prototype.getRootNodes = function (babylonScene, nodes, shouldExportTransformNode) {
-                var rootNodes = [];
-                for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
-                    var babylonTransformNode = nodes_2[_i];
-                    if (shouldExportTransformNode(babylonTransformNode)) {
-                        if (babylonTransformNode.parent == null) {
-                            rootNodes.push(babylonTransformNode);
-                        }
-                    }
-                }
-                return rootNodes;
-            };
             /**
              * Creates a mapping of Node unique id to node index and handles animations
              * @param babylonScene Babylon Scene
@@ -1068,23 +1057,10 @@ var BABYLON;
                 };
                 var idleGLTFAnimations = [];
                 var node;
-                var negScaleRootNode = null;
-                var rootNodes = this.getRootNodes(babylonScene, nodes, shouldExportTransformNode);
-                if (rootNodes.length === 1) {
-                    var node_1 = rootNodes[0];
-                    if (node_1.scaling.equalsToFloats(1, 1, -1)) {
-                        this._convertToRightHandedSystem = !this._convertToRightHandedSystem;
-                        negScaleRootNode = node_1;
-                    }
-                }
-                for (var _i = 0, nodes_3 = nodes; _i < nodes_3.length; _i++) {
-                    var babylonTransformNode = nodes_3[_i];
+                for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
+                    var babylonTransformNode = nodes_2[_i];
                     if (shouldExportTransformNode(babylonTransformNode)) {
                         node = this.createNode(babylonTransformNode, binaryWriter);
-                        if (negScaleRootNode && babylonTransformNode === negScaleRootNode) {
-                            node.scale = [1, 1, 1];
-                            node.rotation = [0, 0, 0, 1];
-                        }
                         var directDescendents = babylonTransformNode.getDescendants(true, function (node) { return (node instanceof BABYLON.TransformNode); });
                         if (directDescendents.length || node.mesh != null) {
                             this._nodes.push(node);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 2
dist/preview release/serializers/babylon.glTF2Serializer.min.js


+ 0 - 1
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -295,7 +295,6 @@ declare module BABYLON.GLTF2 {
          * @param binaryWriter Buffer to write binary data to
          */
         private createSceneAsync(babylonScene, binaryWriter);
-        private getRootNodes(babylonScene, nodes, shouldExportTransformNode);
         /**
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene

+ 7 - 31
dist/preview release/serializers/babylonjs.serializers.js

@@ -1035,7 +1035,7 @@ var BABYLON;
                         for (var _a = 0, _b = bufferMesh.subMeshes; _a < _b.length; _a++) {
                             var submesh = _b[_a];
                             uvCoordsPresent = false;
-                            var babylonMaterial = submesh.getMaterial();
+                            var babylonMaterial = submesh.getMaterial() || bufferMesh.getScene().defaultMaterial;
                             var materialIndex = null;
                             if (babylonMaterial) {
                                 if (bufferMesh instanceof BABYLON.LinesMesh) {
@@ -1052,8 +1052,9 @@ var BABYLON;
                                     materialIndex = this._materials.length - 1;
                                 }
                                 else if (babylonMaterial instanceof BABYLON.MultiMaterial) {
-                                    babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
-                                    if (babylonMaterial) {
+                                    var subMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
+                                    if (subMaterial) {
+                                        babylonMaterial = subMaterial;
                                         materialIndex = this._materialMap[babylonMaterial.uniqueId];
                                     }
                                 }
@@ -1100,7 +1101,7 @@ var BABYLON;
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
                             if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
-                                var sideOrientation = this._babylonScene.materials[materialIndex].sideOrientation;
+                                var sideOrientation = babylonMaterial.sideOrientation;
                                 if (this._convertToRightHandedSystem && sideOrientation === BABYLON.Material.ClockWiseSideOrientation) {
                                     //Overwrite the indices to be counter-clockwise
                                     var byteOffset = indexBufferViewIndex != null ? this._bufferViews[indexBufferViewIndex].byteOffset : null;
@@ -1198,18 +1199,6 @@ var BABYLON;
                     }
                 });
             };
-            _Exporter.prototype.getRootNodes = function (babylonScene, nodes, shouldExportTransformNode) {
-                var rootNodes = [];
-                for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
-                    var babylonTransformNode = nodes_2[_i];
-                    if (shouldExportTransformNode(babylonTransformNode)) {
-                        if (babylonTransformNode.parent == null) {
-                            rootNodes.push(babylonTransformNode);
-                        }
-                    }
-                }
-                return rootNodes;
-            };
             /**
              * Creates a mapping of Node unique id to node index and handles animations
              * @param babylonScene Babylon Scene
@@ -1229,23 +1218,10 @@ var BABYLON;
                 };
                 var idleGLTFAnimations = [];
                 var node;
-                var negScaleRootNode = null;
-                var rootNodes = this.getRootNodes(babylonScene, nodes, shouldExportTransformNode);
-                if (rootNodes.length === 1) {
-                    var node_1 = rootNodes[0];
-                    if (node_1.scaling.equalsToFloats(1, 1, -1)) {
-                        this._convertToRightHandedSystem = !this._convertToRightHandedSystem;
-                        negScaleRootNode = node_1;
-                    }
-                }
-                for (var _i = 0, nodes_3 = nodes; _i < nodes_3.length; _i++) {
-                    var babylonTransformNode = nodes_3[_i];
+                for (var _i = 0, nodes_2 = nodes; _i < nodes_2.length; _i++) {
+                    var babylonTransformNode = nodes_2[_i];
                     if (shouldExportTransformNode(babylonTransformNode)) {
                         node = this.createNode(babylonTransformNode, binaryWriter);
-                        if (negScaleRootNode && babylonTransformNode === negScaleRootNode) {
-                            node.scale = [1, 1, 1];
-                            node.rotation = [0, 0, 0, 1];
-                        }
                         var directDescendents = babylonTransformNode.getDescendants(true, function (node) { return (node instanceof BABYLON.TransformNode); });
                         if (directDescendents.length || node.mesh != null) {
                             this._nodes.push(node);

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 2
dist/preview release/serializers/babylonjs.serializers.min.js


+ 0 - 1
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -302,7 +302,6 @@ declare module BABYLON.GLTF2 {
          * @param binaryWriter Buffer to write binary data to
          */
         private createSceneAsync(babylonScene, binaryWriter);
-        private getRootNodes(babylonScene, nodes, shouldExportTransformNode);
         /**
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 61 - 61
dist/preview release/viewer/babylon.viewer.js


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2707 - 2269
dist/preview release/viewer/babylon.viewer.max.js


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

@@ -75,6 +75,7 @@
 - Added onPoseUpdatedFromDeviceObservable to webVRCamera to detect when the camera's pose has been updated ([TrevorDev](https://github.com/TrevorDev))
 - Added attachToBoxBehavior to attach UI to a bounding box ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo manager's internal gizmos are now public ([TrevorDev](https://github.com/TrevorDev))
+- Ability to customize meshes on gizmos ([TrevorDev](https://github.com/TrevorDev))
 
 ### glTF Loader
 

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

@@ -712,6 +712,7 @@ export class Control {
     /** @hidden */
     public _resetFontCache(): void {
         this._fontSet = true;
+        this._markAsDirty();
     }
 
     /** 

+ 5 - 1
materialsLibrary/src/gradient/babylon.gradientMaterial.ts

@@ -90,6 +90,9 @@ module BABYLON {
         public offset = 0;
 
         @serialize()
+        public scale = 1.0;        
+
+        @serialize()
         public smoothness = 1.0;
 
         @serialize()
@@ -195,7 +198,7 @@ module BABYLON {
                     "vDiffuseInfos",
                     "mBones",
                     "vClipPlane", "diffuseMatrix",
-                    "topColor", "bottomColor", "offset", "smoothness"
+                    "topColor", "bottomColor", "offset", "smoothness", "scale"
                 ];
                 var samplers = ["diffuseSampler"];
                 var uniformBuffers = new Array<string>();
@@ -282,6 +285,7 @@ module BABYLON {
             this._activeEffect.setColor4("topColor", this.topColor, this.topColorAlpha);
             this._activeEffect.setColor4("bottomColor", this.bottomColor, this.bottomColorAlpha);
             this._activeEffect.setFloat("offset", this.offset);
+            this._activeEffect.setFloat("scale", this.scale);
             this._activeEffect.setFloat("smoothness", this.smoothness);
 
             this._afterBind(mesh, this._activeEffect);

+ 2 - 1
materialsLibrary/src/gradient/gradient.fragment.fx

@@ -8,6 +8,7 @@ uniform vec4 vDiffuseColor;
 uniform vec4 topColor;
 uniform vec4 bottomColor;
 uniform float offset;
+uniform float scale;
 uniform float smoothness;
 
 // Input
@@ -52,7 +53,7 @@ void main(void) {
 
 	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
 
-    float h = normalize(vPosition).y + offset;
+    float h = vPosition.y * scale + offset;
     float mysmoothness = clamp(smoothness, 0.01, max(smoothness, 10.));
 
     vec4 baseColor = mix(bottomColor, topColor, max(pow(max(h, 0.0), mysmoothness), 0.0));

+ 7 - 34
serializers/src/glTF/2.0/babylon.glTFExporter.ts

@@ -142,7 +142,6 @@ module BABYLON.GLTF2 {
             this._shouldExportTransformNode = _options.shouldExportTransformNode ? _options.shouldExportTransformNode : (babylonTransformNode: TransformNode) => true;
             this._animationSampleRate = _options.animationSampleRate ? _options.animationSampleRate : 1 / 60;
 
-
             this._glTFMaterialExporter = new _GLTFMaterialExporter(this);
         }
 
@@ -1000,8 +999,8 @@ module BABYLON.GLTF2 {
                     // go through all mesh primitives (submeshes)
                     for (const submesh of bufferMesh.subMeshes) {
                         uvCoordsPresent = false;
-                        let babylonMaterial = submesh.getMaterial();
-
+                        let babylonMaterial = submesh.getMaterial() || bufferMesh.getScene().defaultMaterial;
+                        
                         let materialIndex: Nullable<number> = null;
                         if (babylonMaterial) {
                             if (bufferMesh instanceof LinesMesh) {
@@ -1018,8 +1017,9 @@ module BABYLON.GLTF2 {
                                 materialIndex = this._materials.length - 1;
                             }
                             else if (babylonMaterial instanceof MultiMaterial) {
-                                babylonMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
-                                if (babylonMaterial) {
+                                const subMaterial = babylonMaterial.subMaterials[submesh.materialIndex];
+                                if (subMaterial) {
+                                    babylonMaterial = subMaterial;
                                     materialIndex = this._materialMap[babylonMaterial.uniqueId];
                                 }
                             }
@@ -1068,7 +1068,7 @@ module BABYLON.GLTF2 {
                             meshPrimitive.indices = this._accessors.length - 1;
                         }
                         if (materialIndex != null && Object.keys(meshPrimitive.attributes).length > 0) {
-                            let sideOrientation = this._babylonScene.materials[materialIndex].sideOrientation;
+                            let sideOrientation = babylonMaterial.sideOrientation;
 
                             if (this._convertToRightHandedSystem && sideOrientation === Material.ClockWiseSideOrientation) {
                                 //Overwrite the indices to be counter-clockwise
@@ -1171,18 +1171,6 @@ module BABYLON.GLTF2 {
             });
         }
 
-        private getRootNodes(babylonScene: Scene, nodes: TransformNode[], shouldExportTransformNode: (babylonTransformNode: TransformNode) => boolean): TransformNode[] {
-            const rootNodes: TransformNode[] = [];
-            for (let babylonTransformNode of nodes) {
-                if (shouldExportTransformNode(babylonTransformNode)) {
-                    if (babylonTransformNode.parent == null) {
-                        rootNodes.push(babylonTransformNode);
-                    }
-                }
-            }
-            return rootNodes;
-        }
-
         /**
          * Creates a mapping of Node unique id to node index and handles animations
          * @param babylonScene Babylon Scene
@@ -1202,25 +1190,10 @@ module BABYLON.GLTF2 {
             let idleGLTFAnimations: IAnimation[] = [];
             let node: INode;
 
-            let negScaleRootNode: Nullable<TransformNode> = null;
-
-            const rootNodes = this.getRootNodes(babylonScene, nodes, shouldExportTransformNode);
-            if (rootNodes.length === 1) {
-                const node = rootNodes[0];
-                if (node.scaling.equalsToFloats(1,1, -1)) {
-                    this._convertToRightHandedSystem = !this._convertToRightHandedSystem;
-                    negScaleRootNode = node;
-                }  
-            }
-
             for (let babylonTransformNode of nodes) {
                 if (shouldExportTransformNode(babylonTransformNode)) {
                     node = this.createNode(babylonTransformNode, binaryWriter);
-                    if (negScaleRootNode && babylonTransformNode === negScaleRootNode) {
-                        node.scale = [1,1,1];
-                        node.rotation = [0,0,0,1];
-                    }
-                    
+
                     const directDescendents = babylonTransformNode.getDescendants(true, (node: Node) => {return (node instanceof TransformNode);});
                     if (directDescendents.length || node.mesh != null) {
                         this._nodes.push(node);

+ 84 - 187
src/Engine/babylon.engine.ts

@@ -264,6 +264,11 @@
             }
         }
 
+        /**
+         * Hidden
+         */
+        public static _TextureLoaders: IInternalTextureLoader[] = [];
+
         // Const statics
         /** Defines that alpha blending is disabled */
         public static readonly ALPHA_DISABLE = 0;
@@ -4050,11 +4055,13 @@
          * @param buffer a source of a file previously fetched as either a base64 string, an ArrayBuffer (compressed or image format), HTMLImageElement (image format), or a Blob
          * @param fallback an internal argument in case the function must be called again, due to etc1 not having alpha capabilities
          * @param format internal format.  Default: RGB when extension is '.jpg' else RGBA.  Ignored for compressed textures
+         * @param forcedExtension defines the extension to use to pick the right loader
          * @returns a InternalTexture for assignment back into BABYLON.Texture
          */
         public createTexture(urlArg: Nullable<string>, noMipmap: boolean, invertY: boolean, scene: Nullable<Scene>, samplingMode: number = Engine.TEXTURE_TRILINEAR_SAMPLINGMODE,
             onLoad: Nullable<() => void> = null, onError: Nullable<(message: string, exception: any) => void> = null,
-            buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null): InternalTexture {
+            buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob> = null, fallback: Nullable<InternalTexture> = null, format: Nullable<number> = null,
+            forcedExtension: Nullable<string> = null): InternalTexture {
             var url = String(urlArg); // assign a new string, so that the original is still available in case of fallback
             var fromData = url.substr(0, 5) === "data:";
             var fromBlob = url.substr(0, 5) === "blob:";
@@ -4064,15 +4071,18 @@
 
             // establish the file extension, if possible
             var lastDot = url.lastIndexOf('.');
-            var extension = (lastDot > 0) ? url.substring(lastDot).toLowerCase() : "";
-            var isDDS = this.getCaps().s3tc && (extension.indexOf(".dds") === 0);
-            var isTGA = (extension.indexOf(".tga") === 0);
+            var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? url.substring(lastDot).toLowerCase() : "");
+
+            let loader: Nullable<IInternalTextureLoader> = null;
+            for (let availableLoader of Engine._TextureLoaders) {
+                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, isBase64, buffer ? true : false)) {
+                    loader = availableLoader;
+                    break;
+                }
+            }
 
-            // determine if a ktx file should be substituted
-            var isKTX = false;
-            if (this._textureFormatInUse && !isBase64 && !fallback && !buffer) {
-                url = url.substring(0, lastDot) + this._textureFormatInUse;
-                isKTX = true;
+            if (loader) {
+                url = loader.transformUrl(url, this._textureFormatInUse);
             }
 
             if (scene) {
@@ -4095,79 +4105,54 @@
 
             if (!fallback) this._internalTexturesCache.push(texture);
 
-            var onerror = (message?: string, exception?: any) => {
+            let onInternalError = (message?: string, exception?: any) => {
                 if (scene) {
                     scene._removePendingData(texture);
                 }
 
-                if (onLoadObserver && !isKTX) {
-                    //dont remove the observer if its a ktx file, since the fallback createTexture call will require it.
-                    texture.onLoadedObservable.remove(onLoadObserver);
+                let customFallback = false;
+                if (loader) {
+                    const fallbackUrl = loader.getFallbackTextureUrl(url, this._textureFormatInUse);
+                    if (fallbackUrl) {
+                        // Add Back
+                        customFallback = true;
+                        this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                    }
                 }
 
-                // fallback for when compressed file not found to try again.  For instance, etc1 does not have an alpha capable type
-                if (isKTX) {
-                    this.createTexture(urlArg, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
-                } else if (Tools.UseFallbackTexture) {
-                    this.createTexture(Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                if (!customFallback) {
+                    if (onLoadObserver) {
+                        texture.onLoadedObservable.remove(onLoadObserver);
+                    }
+                    if (Tools.UseFallbackTexture) {
+                        this.createTexture(Tools.fallbackTexture, noMipmap, invertY, scene, samplingMode, null, onError, buffer, texture);
+                    }
                 }
 
                 if (onError) {
                     onError(message || "Unknown error", exception);
                 }
-            };
-
-            var callback: Nullable<(arrayBuffer: any) => void> = null;
+            }
 
             // processing for non-image formats
-            if (isKTX || isTGA || isDDS) {
-                if (isKTX) {
-                    callback = (data) => {
-                        var ktx = new KhronosTextureContainer(data, 1);
-
-                        this._prepareWebGLTexture(texture, scene, ktx.pixelWidth, ktx.pixelHeight, invertY, false, true, () => {
-                            ktx.uploadLevels(texture, !noMipmap);
-                            return false;
-                        }, samplingMode);
-                    };
-                } else if (isTGA) {
-                    callback = (arrayBuffer) => {
-                        var data = new Uint8Array(arrayBuffer);
-
-                        var header = TGATools.GetTGAHeader(data);
-
-                        this._prepareWebGLTexture(texture, scene, header.width, header.height, invertY, noMipmap, false, () => {
-                            TGATools.UploadContent(texture, data);
-                            return false;
-                        }, samplingMode);
-                    };
-
-                } else if (isDDS) {
-                    callback = (data) => {
-                        var info = DDSTools.GetDDSInfo(data);
-
-                        var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) === 1);
-                        this._prepareWebGLTexture(texture, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, () => {
-                            DDSTools.UploadDDSLevels(this, texture, data, info, loadMipmap, 1);
-                            return false;
-                        }, samplingMode);
-                    };
+            if (loader) {
+                var callback = (data: string | ArrayBuffer) => {
+                    loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => {
+                        this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
+                                done();
+                                return false;
+                            }, 
+                            samplingMode);
+                    });
                 }
 
                 if (!buffer) {
-                    this._loadFile(url, data => {
-                        if (callback) {
-                            callback(data);
-                        }
-                    }, undefined, scene ? scene.database : undefined, true, (request?: XMLHttpRequest, exception?: any) => {
-                        onerror("Unable to load " + (request ? request.responseURL : url, exception));
+                    this._loadFile(url, callback, undefined, scene ? scene.database : undefined, true, (request?: XMLHttpRequest, exception?: any) => {
+                        onInternalError("Unable to load " + (request ? request.responseURL : url, exception));
                     });
                 } else {
-                    if (callback) {
-                        callback(buffer);
-                    }
+                    callback(buffer as ArrayBuffer);
                 }
-                // image format processing
             } else {
                 var onload = (img: HTMLImageElement) => {
                     if (fromBlob && !this._doNotHandleContextLost) {
@@ -4231,11 +4216,11 @@
                     if (buffer instanceof HTMLImageElement) {
                         onload(buffer);
                     } else {
-                        Tools.LoadImage(url, onload, onerror, scene ? scene.database : null);
+                        Tools.LoadImage(url, onload, onInternalError, scene ? scene.database : null);
                     }
                 }
                 else if (typeof buffer === "string" || buffer instanceof ArrayBuffer || buffer instanceof Blob) {
-                    Tools.LoadImage(buffer, onload, onerror, scene ? scene.database : null);
+                    Tools.LoadImage(buffer, onload, onInternalError, scene ? scene.database : null);
                 }
                 else {
                     onload(<HTMLImageElement>buffer);
@@ -5501,142 +5486,50 @@
                 texture._files = files;
             }
 
-            var isKTX = false;
-            var isDDS = false;
-            var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
-            if (this._textureFormatInUse && !fallback) {
-                rootUrl = (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + this._textureFormatInUse;
-                isKTX = true;
-            } else {
-                isDDS = (extension === ".dds");
-                isEnv = (extension === ".env");
+
+            let loader: Nullable<IInternalTextureLoader> = null;
+            for (let availableLoader of Engine._TextureLoaders) {
+                if (availableLoader.canLoad(extension, this._textureFormatInUse, fallback, false, false)) {
+                    loader = availableLoader;
+                    break;
+                }
             }
 
-            let onerror = (request?: XMLHttpRequest, exception?: any) => {
-                if(isKTX){
-                    //remove the format appended to the rootUrl in the original createCubeTexture call.
-                    var exp = new RegExp("" + this._textureFormatInUse + "$");
-                    this.createCubeTexture(rootUrl.replace(exp, ""), scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+            let onInternalError = (request?: XMLHttpRequest, exception?: any) => {
+                if (loader) {
+                    const fallbackUrl = loader.getFallbackTextureUrl(rootUrl, this._textureFormatInUse);
+                    if (fallbackUrl) {
+                        this.createCubeTexture(fallbackUrl, scene, files, noMipmap, onLoad, onError, format, extension, createPolynomials, lodScale, lodOffset, texture);
+                    }
                 }
+
                 if (onError && request) {
                     onError(request.status + " " + request.statusText, exception);
                 }
             }
 
-            if (isKTX) {
-                this._loadFile(rootUrl, data => {
-                    var ktx = new KhronosTextureContainer(data, 6);
-
-                    var loadMipmap = ktx.numberOfMipmapLevels > 1 && !noMipmap;
+            if (loader) {
+                rootUrl = loader.transformUrl(rootUrl, this._textureFormatInUse);
 
+                const onloaddata = (data: any) => {
                     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
-                    this._unpackFlipY(true);
-
-                    ktx.uploadLevels(texture, !noMipmap);
-
-                    this.setCubeMapTextureParams(gl, loadMipmap);
-
-                    texture.width = ktx.pixelWidth;
-                    texture.height = ktx.pixelHeight;
-                    texture.isReady = true;
-                }, undefined, undefined, true, onerror);
-            }
-            else if (isEnv) {
-                this._loadFile(rootUrl, (data) => {
-                    data = data as ArrayBuffer;
-                    var info = EnvironmentTextureTools.GetEnvInfo(data);
-                    if (info) {
-                        texture.width = info.width;
-                        texture.height = info.width;
-
-                        EnvironmentTextureTools.UploadEnvSpherical(texture, info);
-                        EnvironmentTextureTools.UploadEnvLevelsAsync(texture, data, info).then(() => {
-                            texture.isReady = true;
-                            if (onLoad) {
-                                onLoad();
-                            }
-                        });
+                    loader!.loadCubeData(data, texture, createPolynomials, onLoad, onError);
+                };
+                if (files && files.length === 6) {
+                    if (loader.supportCascades) {
+                        this._cascadeLoadFiles(scene, onloaddata, files, onError);
                     }
                     else if (onError) {
-                        onError("Can not parse the environment file", null);
+                        onError("Textures type does not support cascades.");
                     }
-                }, undefined, undefined, true, onerror);
-            }
-            else if (isDDS) {
-                if (files && files.length === 6) {
-                    this._cascadeLoadFiles(
-                        scene,
-                        imgs => {
-                            var info: DDSInfo | undefined;
-                            var loadMipmap: boolean = false;
-                            var width: number = 0;
-                            for (let index = 0; index < imgs.length; index++) {
-                                let data = imgs[index];
-                                info = DDSTools.GetDDSInfo(data);
-
-                                texture.width = info.width;
-                                texture.height = info.height;
-                                width = info.width;
-
-                                loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
-
-                                this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
-                                this._unpackFlipY(info.isCompressed);
-
-                                DDSTools.UploadDDSLevels(this, texture, data, info, loadMipmap, 6, -1, index);
-
-                                if (!noMipmap && !info.isFourCC && info.mipmapCount === 1) {
-                                    gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                                }
-                            }
-
-                            this.setCubeMapTextureParams(gl, loadMipmap);
-                            texture.isReady = true;
-
-                            if (onLoad) {
-                                onLoad({ isDDS: true, width: width, info, imgs, texture });
-                            }
-                        },
-                        files,
-                        onError);
-                } else {
-                    this._loadFile(rootUrl,
-                        data => {
-                            var info = DDSTools.GetDDSInfo(data);
-
-                            texture.width = info.width;
-                            texture.height = info.height;
-
-                            if (createPolynomials) {
-                                info.sphericalPolynomial = new SphericalPolynomial();
-                            }
-
-                            var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
-
-                            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
-                            this._unpackFlipY(info.isCompressed);
-
-                            DDSTools.UploadDDSLevels(this, texture, data, info, loadMipmap, 6);
-
-                            if (!noMipmap && !info.isFourCC && info.mipmapCount === 1) {
-                                gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
-                            }
-
-                            this.setCubeMapTextureParams(gl, loadMipmap);
-                            texture.isReady = true;
-
-                            if (onLoad) {
-                                onLoad({ isDDS: true, width: info.width, info, data, texture });
-                            }
-                        },
-                        undefined,
-                        undefined,
-                        true,
-                        onerror);
                 }
-            } else {
+                else {
+                    this._loadFile(rootUrl, onloaddata, undefined, undefined, true, onInternalError);
+                }
+            }
+            else {
                 if (!files) {
                     throw new Error("Cannot load cubemap because files were not defined");
                 }
@@ -5671,7 +5564,7 @@
                         gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                     }
 
-                    this.setCubeMapTextureParams(gl, !noMipmap);
+                    this._setCubeMapTextureParams(!noMipmap);
 
                     texture.width = width;
                     texture.height = height;
@@ -5694,7 +5587,11 @@
             return texture;
         }
 
-        private setCubeMapTextureParams(gl: WebGLRenderingContext, loadMipmap: boolean) {
+        /**
+         * @hidden
+         */
+        public _setCubeMapTextureParams(loadMipmap: boolean): void {
+            var gl = this._gl;
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, loadMipmap ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR);
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);

+ 3 - 0
src/Gizmos/babylon.axisDragGizmo.ts

@@ -82,6 +82,9 @@ module BABYLON {
             })
 
             this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState)=>{
+                if(this._customMeshSet){
+                    return;
+                }
                 if(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1)){
                     this._rootMesh.getChildMeshes().forEach((m)=>{
                         m.material = hoverMaterial;

+ 4 - 1
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -90,6 +90,9 @@ module BABYLON {
             })
 
             this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState)=>{
+                if(this._customMeshSet){
+                    return;
+                }
                 if(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1)){
                     this._rootMesh.getChildMeshes().forEach((m)=>{
                         m.material = hoverMaterial;
@@ -116,6 +119,6 @@ module BABYLON {
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this.dragBehavior.detach();
             super.dispose();
-        } 
+        }
     }
 }

+ 8 - 1
src/Gizmos/babylon.boundingBoxGizmo.ts

@@ -264,7 +264,7 @@ module BABYLON {
         }
 
         /**
-         * Update the bounding box of the gizmo
+         * Updates the bounding box information for the Gizmo
          */
         public updateBoundingBox() {
             if (this.attachedMesh) {
@@ -426,5 +426,12 @@ module BABYLON {
             box.visibility = 0;
             return box;
         }
+        /**
+         * CustomMeshes are not supported by this gizmo
+         * @param mesh The mesh to replace the default mesh of the gizmo
+         */
+        public setCustomMesh(mesh: Mesh) {
+            Tools.Error("Custom meshes are not supported on this gizmo");
+        }
     }
 }

+ 20 - 0
src/Gizmos/babylon.gizmo.ts

@@ -11,6 +11,10 @@ module BABYLON {
         private _scaleFactor = 3;
         private _tmpMatrix = new Matrix();
         /**
+         * If a custom mesh has been set (Default: false)
+         */
+        protected _customMeshSet = false;
+        /**
          * Mesh that the gizmo will be attached to. (eg. on a drag gizmo the mesh that will be dragged)
          * * When set, interactions will be enabled
          */
@@ -22,6 +26,22 @@ module BABYLON {
             this._rootMesh.setEnabled(value?true:false);
             this._attachedMeshChanged(value);
         }
+
+        /**
+         * Disposes and replaces the current meshes in the gizmo with the specified mesh
+         * @param mesh The mesh to replace the default mesh of the gizmo
+         */
+        public setCustomMesh(mesh:Mesh){
+            if(mesh.getScene() != this.gizmoLayer.utilityLayerScene){
+                throw "When setting a custom mesh on a gizmo, the custom meshes scene must be the same as the gizmos (eg. gizmo.gizmoLayer.utilityLayerScene)";
+            }
+            this._rootMesh.getChildMeshes().forEach((c)=>{
+                c.dispose();
+            })
+            mesh.parent = this._rootMesh;
+            this._customMeshSet = true;
+        }
+
         /**
          * If set the gizmo's rotation will be updated to match the attached mesh each frame (Default: true)
          */

+ 4 - 1
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -133,6 +133,9 @@ module BABYLON {
             })
 
             this._pointerObserver = gizmoLayer.utilityLayerScene.onPointerObservable.add((pointerInfo, eventState)=>{
+                if(this._customMeshSet){
+                    return;
+                }
                 if(pointerInfo.pickInfo && (this._rootMesh.getChildMeshes().indexOf(<Mesh>pointerInfo.pickInfo.pickedMesh) != -1)){
                     this._rootMesh.getChildMeshes().forEach((m)=>{
                         m.material = hoverMaterial;
@@ -159,6 +162,6 @@ module BABYLON {
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
             this.dragBehavior.detach();
             super.dispose();
-        } 
+        }
     }
 }

+ 8 - 0
src/Gizmos/babylon.positionGizmo.ts

@@ -54,5 +54,13 @@ module BABYLON {
             this.yGizmo.dispose();
             this.zGizmo.dispose();
         }
+
+        /**
+         * CustomMeshes are not supported by this gizmo
+         * @param mesh The mesh to replace the default mesh of the gizmo
+         */
+        public setCustomMesh(mesh:Mesh){
+            Tools.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo)");
+        }
     }
 }

+ 8 - 0
src/Gizmos/babylon.rotationGizmo.ts

@@ -54,5 +54,13 @@ module BABYLON {
             this.yGizmo.dispose();
             this.zGizmo.dispose();
         }
+
+        /**
+         * CustomMeshes are not supported by this gizmo
+         * @param mesh The mesh to replace the default mesh of the gizmo
+         */
+        public setCustomMesh(mesh:Mesh){
+            Tools.Error("Custom meshes are not supported on this gizmo, please set the custom meshes on the gizmos contained within this one (gizmo.xGizmo, gizmo.yGizmo, gizmo.zGizmo)");
+        }
     }
 }

+ 123 - 0
src/Materials/Textures/Loaders/babylon.ddsTextureLoader.ts

@@ -0,0 +1,123 @@
+module BABYLON {
+    /**
+     * Implementation of the DDS Texture Loader.
+     */
+    class DDSTextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = true;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            return extension.indexOf(".dds") === 0;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            return rootUrl;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            return null;
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(imgs: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            var engine = texture.getEngine();
+            var info: DDSInfo | undefined;
+            var loadMipmap: boolean = false;
+            if (Array.isArray(imgs)) {
+                for (let index = 0; index < imgs.length; index++) {
+                    let data = imgs[index];
+                    info = DDSTools.GetDDSInfo(data);
+
+                    texture.width = info.width;
+                    texture.height = info.height;
+
+                    loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && texture.generateMipMaps;
+
+                    engine._unpackFlipY(info.isCompressed);
+
+                    DDSTools.UploadDDSLevels(engine, texture, data, info, loadMipmap, 6, -1, index);
+
+                    if (!info.isFourCC && info.mipmapCount === 1) {
+                        engine.generateMipMapsForCubemap(texture);
+                    }
+                }
+            }
+            else {
+                var data = imgs;
+                info = DDSTools.GetDDSInfo(data);
+
+                texture.width = info.width;
+                texture.height = info.height;
+
+                if (createPolynomials) {
+                    info.sphericalPolynomial = new SphericalPolynomial();
+                }
+
+                loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && texture.generateMipMaps;
+                engine._unpackFlipY(info.isCompressed);
+
+                DDSTools.UploadDDSLevels(engine, texture, data, info, loadMipmap, 6);
+
+                if (!info.isFourCC && info.mipmapCount === 1) {
+                    engine.generateMipMapsForCubemap(texture);
+                }
+            }
+
+            engine._setCubeMapTextureParams(loadMipmap);
+            texture.isReady = true;
+
+            if (onLoad) {
+                onLoad({ isDDS: true, width: texture.width, info, imgs, texture });
+            }
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            var info = DDSTools.GetDDSInfo(data);
+
+            var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && texture.generateMipMaps && ((info.width >> (info.mipmapCount - 1)) === 1);
+            callback(info.width, info.height, !loadMipmap, info.isFourCC, () => {
+                DDSTools.UploadDDSLevels(texture.getEngine(), texture, data, info, loadMipmap, 1);
+            });
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.push(new DDSTextureLoader());
+}

+ 90 - 0
src/Materials/Textures/Loaders/babylon.envTextureLoader.ts

@@ -0,0 +1,90 @@
+module BABYLON {
+    /**
+     * Implementation of the ENV Texture Loader.
+     */
+    class ENVTextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = false;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            return extension.indexOf(".env") === 0;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            return rootUrl;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            return null;
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            if (Array.isArray(data)) {
+                return;
+            }
+
+            data = data as ArrayBuffer;
+            var info = EnvironmentTextureTools.GetEnvInfo(data);
+            if (info) {
+                texture.width = info.width;
+                texture.height = info.width;
+
+                EnvironmentTextureTools.UploadEnvSpherical(texture, info);
+                EnvironmentTextureTools.UploadEnvLevelsAsync(texture, data, info).then(() => {
+                    texture.isReady = true;
+                    if (onLoad) {
+                        onLoad();
+                    }
+                });
+            }
+            else if (onError) {
+                onError("Can not parse the environment file", null);
+            }
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            throw ".env not supported in 2d.";
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.push(new ENVTextureLoader());
+}

+ 97 - 0
src/Materials/Textures/Loaders/babylon.ktxTextureLoader.ts

@@ -0,0 +1,97 @@
+module BABYLON {
+    /**
+     * Implementation of the KTX Texture Loader.
+     */
+    class KTXTextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = false;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            if (textureFormatInUse && !isBase64 && !fallback && !isBuffer) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            var lastDot = rootUrl.lastIndexOf('.');
+            return (lastDot > -1 ? rootUrl.substring(0, lastDot) : rootUrl) + textureFormatInUse;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            // remove the format appended to the rootUrl in the original createCubeTexture call.
+            var exp = new RegExp("" + textureFormatInUse! + "$");
+            return rootUrl.replace(exp, "");
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            if (Array.isArray(data)) {
+                return;
+            }
+
+            var engine = texture.getEngine();
+            var ktx = new KhronosTextureContainer(data, 6);
+
+            var loadMipmap = ktx.numberOfMipmapLevels > 1 && texture.generateMipMaps;
+
+            engine._unpackFlipY(true);
+
+            ktx.uploadLevels(texture, texture.generateMipMaps);
+
+            texture.width = ktx.pixelWidth;
+            texture.height = ktx.pixelHeight;
+
+            engine._setCubeMapTextureParams(loadMipmap);
+            texture.isReady = true;
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            var ktx = new KhronosTextureContainer(data, 1);
+
+            callback(ktx.pixelWidth, ktx.pixelHeight, false, true, () => {
+                ktx.uploadLevels(texture, texture.generateMipMaps);
+            });
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.unshift(new KTXTextureLoader());
+}

+ 75 - 0
src/Materials/Textures/Loaders/babylon.tgaTextureLoader.ts

@@ -0,0 +1,75 @@
+module BABYLON {
+    /**
+     * Implementation of the TGA Texture Loader.
+     */
+    class TGATextureLoader implements IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        public readonly supportCascades = false;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        public canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean {
+            return extension.indexOf(".tga") === 0;
+        }
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        public transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string {
+            return rootUrl;
+        }
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        public getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string> {
+            return null;
+        }
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        public loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void {
+            throw ".env not supported in Cube.";
+        }
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        public loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void {
+            var uintData = new Uint8Array(data);
+
+            var header = TGATools.GetTGAHeader(uintData);
+            callback(header.width, header.height, texture.generateMipMaps, false, () => {
+                TGATools.UploadContent(texture, uintData);
+            });
+        }
+    }
+
+    // Register the loader.
+    Engine._TextureLoaders.push(new TGATextureLoader());
+}

+ 6 - 14
src/Materials/Textures/Procedurals/babylon.noiseProceduralTexture.ts

@@ -3,25 +3,19 @@ module BABYLON {
      * Class used to generate noise procedural textures
      */
     export class NoiseProceduralTexture extends ProceduralTexture {
-        private _time = new Vector2();
+        private _time = 0;
 
         /** Gets or sets a value between 0 and 1 indicating the overall brightness of the texture (default is 0.2) */
         public brightness = 0.2;
 
-        /** Defines the first octave to start from (default is 3) */
-        public firstOctave = 3;
-
         /** Defines the number of octaves to process */
-        public octaves = 8;
+        public octaves = 3;
 
         /** Defines the level of persistence (0.8 by default) */
         public persistence = 0.8;
 
-        /** Gets or sets animation speed factor for X axis (default is 1) */
-        public animationSpeedFactorX = 1;
-
-        /** Gets or sets animation speed factor for Y axis (default is 1) */
-        public animationSpeedFactorY = 1;
+        /** Gets or sets animation speed factor (default is 1) */
+        public animationSpeedFactor = 1;
 
         /**
          * Creates a new NoiseProceduralTexture
@@ -43,14 +37,12 @@ module BABYLON {
                 return;
             }
 
-            this._time.x += scene.getAnimationRatio() * this.animationSpeedFactorX * 0.001;
-            this._time.y += scene.getAnimationRatio() * this.animationSpeedFactorY * 0.001;
+            this._time += scene.getAnimationRatio() * this.animationSpeedFactor * 0.01;
 
             this.setFloat("brightness", this.brightness);
-            this.setInt("firstOctave", this.firstOctave);
             this.setInt("octaves", this.octaves);
             this.setFloat("persistence", this.persistence);
-            this.setVector2("timeScale", this._time);
+            this.setFloat("timeScale", this._time);
         }
 
         /** Generate the current state of the procedural texture */

+ 57 - 0
src/Materials/Textures/babylon.internalTextureLoader.ts

@@ -0,0 +1,57 @@
+module BABYLON {
+    /**
+     * This represents the required contract to create a new type of texture loader.
+     */
+    export interface IInternalTextureLoader {
+        /**
+         * Defines wether the loader supports cascade loading the different faces.
+         */
+        supportCascades: boolean;
+
+        /**
+         * This returns if the loader support the current file information.
+         * @param extension defines the file extension of the file being loaded
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @param fallback defines the fallback internal texture if any
+         * @param isBase64 defines whether the texture is encoded as a base64
+         * @param isBuffer defines whether the texture data are stored as a buffer
+         * @returns true if the loader can load the specified file
+         */
+        canLoad(extension: string, textureFormatInUse: Nullable<string>, fallback: Nullable<InternalTexture>, isBase64: boolean, isBuffer: boolean): boolean;
+
+        /**
+         * Transform the url before loading if required.
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the transformed texture
+         */
+        transformUrl(rootUrl: string, textureFormatInUse: Nullable<string>): string;
+
+        /**
+         * Gets the fallback url in case the load fail. This can return null to allow the default fallback mecanism to work
+         * @param rootUrl the url of the texture
+         * @param textureFormatInUse defines the current compressed format in use iun the engine
+         * @returns the fallback texture
+         */
+        getFallbackTextureUrl(rootUrl: string, textureFormatInUse: Nullable<string>): Nullable<string>;
+
+        /**
+         * Uploads the cube texture data to the WebGl Texture. It has alreday been bound.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param createPolynomials will be true if polynomials have been requested
+         * @param onLoad defines the callback to trigger once the texture is ready
+         * @param onError defines the callback to trigger in case of error
+         */
+        loadCubeData(data: string | ArrayBuffer | (string | ArrayBuffer)[], texture: InternalTexture, createPolynomials: boolean, onLoad: Nullable<(data?: any) => void>, onError: Nullable<(message?: string, exception?: any) => void>): void;
+
+        /**
+         * Uploads the 2D texture data to the WebGl Texture. It has alreday been bound once in the callback.
+         * @param data contains the texture data
+         * @param texture defines the BabylonJS internal texture
+         * @param callback defines the method to call once ready to upload
+         */
+        loadData(data: ArrayBuffer, texture: InternalTexture, 
+            callback: (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void) => void): void;
+    }
+}

+ 8 - 9
src/Particles/EmitterTypes/babylon.sphereParticleEmitter.ts

@@ -4,22 +4,21 @@ module BABYLON {
      * It emits the particles alongside the sphere radius. The emission direction might be randomized.
      */
     export class SphereParticleEmitter implements IParticleEmitterType {
-
-        /**
-         * Gets or sets a value indicating where on the radius the start position should be picked (1 = everywhere, 0 = only surface)
-         */
-        public radiusRange = 1;
-
-        /**
+         /**
          * Creates a new instance SphereParticleEmitter
          * @param radius the radius of the emission sphere (1 by default)
+         * @param radiusRange the range of the emission sphere [0-1] 0 Surface only, 1 Entire Radius (1 by default) 
          * @param directionRandomizer defines how much to randomize the particle direction [0-1]
          */
         constructor(
             /**
              * The radius of the emission sphere.
              */
-            public radius = 1, 
+            public radius = 1,
+            /**
+             * The range of emission [0-1] 0 Surface only, 1 Entire Radius.
+             */
+            public radiusRange = 1,
             /**
              * How much to randomize the particle direction [0-1].
              */
@@ -224,4 +223,4 @@ module BABYLON {
             this.direction2.copyFrom(serializationObject.direction2);
         }           
     }
-}
+}

+ 8 - 0
src/Particles/babylon.IParticleSystem.ts

@@ -175,6 +175,14 @@ module BABYLON {
 
         /** Gets or sets a Vector2 used to move the pivot (by default (0,0)) */
         translationPivot: Vector2;
+
+        /**
+         * Gets or sets a texture used to add random noise to particle positions
+         */
+        noiseTexture: Nullable<BaseTexture>;
+
+        /** Gets or sets the strength to apply to the noise value (default is (10, 10, 10)) */
+        noiseStrength: Vector3;        
         
         /**
          * Gets or sets the billboard mode to use when isBillboardBased = true.

+ 27 - 5
src/Particles/babylon.gpuParticleSystem.ts

@@ -328,6 +328,14 @@
 
         /** Gets or sets a Vector2 used to move the pivot (by default (0,0)) */
         public translationPivot = new Vector2(0, 0);     
+
+        /**
+         * Gets or sets a texture used to add random noise to particle positions
+         */
+        public noiseTexture: Nullable<BaseTexture>;
+
+        /** Gets or sets the strength to apply to the noise value (default is (10, 10, 10)) */
+        public noiseStrength = new Vector3(10, 10, 10);        
         
         /**
          * Gets or sets the billboard mode to use when isBillboardBased = true.
@@ -718,9 +726,9 @@
                 attributes: ["position", "age", "life", "seed", "size", "color", "direction", "initialDirection", "angle", "cellIndex"],
                 uniformsNames: ["currentCount", "timeDelta", "emitterWM", "lifeTime", "color1", "color2", "sizeRange", "scaleRange","gravity", "emitPower",
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
-                                "angleRange", "radiusRange", "cellInfos"],
+                                "angleRange", "radiusRange", "cellInfos", "noiseStrength"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler", "velocityGradientSampler", "noiseSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -972,7 +980,11 @@
             
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
-            }             
+            }   
+            
+            if (this.noiseTexture) {
+                defines += "\n#define NOISE";
+            }
 
             if (this._updateEffect && this._updateEffectOptions.defines === defines) {
                 return;
@@ -1200,7 +1212,12 @@
             }
             if (this._isAnimationSheetEnabled) {
                 this._updateEffect.setFloat3("cellInfos", this.startSpriteCellID, this.endSpriteCellID, this.spriteCellChangeSpeed);
-            }            
+            }        
+            
+            if (this.noiseTexture) {
+                this._updateEffect.setTexture("noiseSampler", this.noiseTexture);  
+                this._updateEffect.setVector3("noiseStrength", this.noiseStrength);    
+            }
 
             let emitterWM: Matrix;
             if ((<AbstractMesh>this.emitter).position) {
@@ -1379,7 +1396,12 @@
             if (disposeTexture && this.particleTexture) {
                 this.particleTexture.dispose();
                 this.particleTexture = null;
-            }            
+            }    
+            
+            if (disposeTexture && this.noiseTexture) {
+                this.noiseTexture.dispose();
+                this.noiseTexture = null;
+            }
 
             // Callback
             this.onDisposeObservable.notifyObservers(this);

+ 87 - 17
src/Particles/babylon.particleSystem.ts

@@ -157,6 +157,14 @@
         public preventAutoStart: boolean = false;
 
         /**
+         * Gets or sets a texture used to add random noise to particle positions
+         */
+        public noiseTexture: Nullable<BaseTexture>;
+
+        /** Gets or sets the strength to apply to the noise value (default is (10, 10, 10)) */
+        public noiseStrength = new Vector3(10, 10, 10);
+
+        /**
          * This function can be defined to provide custom update for active particles.
          * This function will be called instead of regular update (age, position, color, etc.).
          * Do not forget that this function will be called on every frame so try to keep it simple and fast :)
@@ -184,6 +192,8 @@
          */
         public gravity = Vector3.Zero();
 
+        private _emitterWorldMatrix: Matrix;
+
         private _colorGradients: Nullable<Array<ColorGradient>> = null;
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
         private _lifeTimeGradients: Nullable<Array<FactorGradient>> = null;
@@ -511,6 +521,14 @@
             this.particleEmitterType = new BoxParticleEmitter();
 
             this.updateFunction = (particles: Particle[]): void => {
+                let noiseTextureData: Nullable<Uint8Array> = null;
+                let noiseTextureSize: Nullable<ISize> = null;
+
+                if (this.noiseTexture) { // We need to get texture data back to CPU
+                    noiseTextureData = <Nullable<Uint8Array>>(this.noiseTexture.readPixels());
+                    noiseTextureSize = this.noiseTexture.getSize();
+                }
+
                 for (var index = 0; index < particles.length; index++) {
                     var particle = particles[index];
                     particle.age += this._scaledUpdateSpeed;
@@ -544,6 +562,7 @@
                             }
                         }
 
+                        // Angular speed
                         if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {                  
                             Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
                                 if (currentGradient !== particle._currentAngularSpeedGradient) {
@@ -556,6 +575,7 @@
                         }                        
                         particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
 
+                        // Direction
                         let directionScale = this._scaledUpdateSpeed;
                         if (this._velocityGradients && this._velocityGradients.length > 0) {                  
                             Tools.GetCurrentGradient(ratio, this._velocityGradients, (currentGradient, nextGradient, scale) => {
@@ -570,6 +590,28 @@
                         particle.direction.scaleToRef(directionScale, this._scaledDirection);
                         particle.position.addInPlace(this._scaledDirection);
 
+                        // Noise
+                        if (noiseTextureData && noiseTextureSize) {
+                            let localPosition = Tmp.Vector3[0];
+                            let emitterPosition = Tmp.Vector3[1];
+
+                            this._emitterWorldMatrix.getTranslationToRef(emitterPosition);
+                            particle.position.subtractToRef(emitterPosition, localPosition);
+
+                            let fetchedColorR = this._fetchR(localPosition.y, localPosition.z, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
+                            let fetchedColorG = this._fetchR(localPosition.x + 0.33, localPosition.z + 0.33, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
+                            let fetchedColorB = this._fetchR(localPosition.x - 0.33, localPosition.y - 0.33, noiseTextureSize.width, noiseTextureSize.height, noiseTextureData);
+                            
+                            let force = Tmp.Vector3[0];
+                            let scaledForce = Tmp.Vector3[1];
+
+                            force.copyFromFloats((2 * fetchedColorR - 1) * this.noiseStrength.x, (2 * fetchedColorG - 1) * this.noiseStrength.y, (2 * fetchedColorB - 1) * this.noiseStrength.z);
+
+                            force.scaleToRef(this._scaledUpdateSpeed, scaledForce);
+                            particle.direction.addInPlace(scaledForce);
+                        }
+
+                        // Gravity
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
 
@@ -593,6 +635,17 @@
             }
         }
 
+        private _fetchR(u: number, v: number, width: number, height: number, pixels: Uint8Array): number {
+            u = Math.abs(u) * 0.5 + 0.5;
+            v = Math.abs(v) * 0.5 + 0.5;
+
+            let wrappedU = ((u * width) % width) | 0;
+            let wrappedV = ((v * height) % height) | 0;
+
+            let position = (wrappedU + wrappedV * width) * 4;
+            return pixels[position] / 255;
+        }          
+
         private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number, factor2?: number) {
             let newGradient = new FactorGradient();
             newGradient.gradient = gradient;
@@ -1068,19 +1121,17 @@
             // Update current
             this._alive = this._particles.length > 0;
 
-            this.updateFunction(this._particles);
-
-            // Add new ones
-            var worldMatrix;
-
             if ((<AbstractMesh>this.emitter).position) {
                 var emitterMesh = (<AbstractMesh>this.emitter);
-                worldMatrix = emitterMesh.getWorldMatrix();
+                this._emitterWorldMatrix = emitterMesh.getWorldMatrix();
             } else {
                 var emitterPosition = (<Vector3>this.emitter);
-                worldMatrix = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
+                this._emitterWorldMatrix = Matrix.Translation(emitterPosition.x, emitterPosition.y, emitterPosition.z);
             }
 
+            this.updateFunction(this._particles);
+
+            // Add new ones
             var particle: Particle;
             for (var index = 0; index < newParticles; index++) {
                 if (this._particles.length === this._capacity) {
@@ -1095,17 +1146,17 @@
                 let emitPower = Scalar.RandomRange(this.minEmitPower, this.maxEmitPower);
 
                 if (this.startPositionFunction) {
-                    this.startPositionFunction(worldMatrix, particle.position, particle);
+                    this.startPositionFunction(this._emitterWorldMatrix, particle.position, particle);
                 }
                 else {
-                    this.particleEmitterType.startPositionFunction(worldMatrix, particle.position, particle);
+                    this.particleEmitterType.startPositionFunction(this._emitterWorldMatrix, particle.position, particle);
                 }
 
                 if (this.startDirectionFunction) {
-                    this.startDirectionFunction(worldMatrix, particle.direction, particle);
+                    this.startDirectionFunction(this._emitterWorldMatrix, particle.direction, particle);
                 }
                 else {
-                    this.particleEmitterType.startDirectionFunction(worldMatrix, particle.direction, particle);
+                    this.particleEmitterType.startDirectionFunction(this._emitterWorldMatrix, particle.direction, particle);
                 }
 
                 if (emitPower === 0) {
@@ -1496,6 +1547,11 @@
                 this.particleTexture = null;
             }
 
+            if (disposeTexture && this.noiseTexture) {
+                this.noiseTexture.dispose();
+                this.noiseTexture = null;
+            }
+
             this._removeFromRoot();
 
             // Remove from scene
@@ -1512,10 +1568,11 @@
         /**
          * Creates a Sphere Emitter for the particle system. (emits along the sphere radius)
          * @param radius The radius of the sphere to emit from
+         * @param radiusRange The range of the sphere to emit from [0-1] 0 Surface Only, 1 Entire Radius
          * @returns the emitter
          */
-        public createSphereEmitter(radius = 1): SphereParticleEmitter {
-            var particleEmitter = new SphereParticleEmitter(radius);
+        public createSphereEmitter(radius = 1, radiusRange = 1): SphereParticleEmitter {
+            var particleEmitter = new SphereParticleEmitter(radius, radiusRange);
             this.particleEmitterType = particleEmitter;
             return particleEmitter;
         }
@@ -1644,7 +1701,7 @@
             if (particleSystem.particleTexture) {
                 serializationObject.textureName = particleSystem.particleTexture.name;
             }
-            
+           
             // Animations
             Animation.AppendSerializedAnimations(particleSystem, serializationObject);
 
@@ -1665,6 +1722,7 @@
             serializationObject.maxLifeTime = particleSystem.maxLifeTime;
             serializationObject.emitRate = particleSystem.emitRate;
             serializationObject.gravity = particleSystem.gravity.asArray();
+            serializationObject.noiseStrength = particleSystem.noiseStrength.asArray();
             serializationObject.color1 = particleSystem.color1.asArray();
             serializationObject.color2 = particleSystem.color2.asArray();
             serializationObject.colorDead = particleSystem.colorDead.asArray();
@@ -1750,7 +1808,12 @@
 
                     serializationObject.velocityGradients.push(serializedGradient);
                 }
-            }              
+            }    
+            
+            if (particleSystem.noiseTexture && particleSystem.noiseTexture instanceof ProceduralTexture) {
+                const noiseTexture = particleSystem.noiseTexture as ProceduralTexture;
+                serializationObject.noiseTexture = noiseTexture.serialize();
+            }
         }
 
         /** @hidden */
@@ -1821,6 +1884,9 @@
             particleSystem.maxEmitPower = parsedParticleSystem.maxEmitPower;
             particleSystem.emitRate = parsedParticleSystem.emitRate;
             particleSystem.gravity = Vector3.FromArray(parsedParticleSystem.gravity);
+            if (parsedParticleSystem.noiseStrength) {
+                particleSystem.noiseStrength = Vector3.FromArray(parsedParticleSystem.noiseStrength);
+            }
             particleSystem.color1 = Color4.FromArray(parsedParticleSystem.color1);
             particleSystem.color2 = Color4.FromArray(parsedParticleSystem.color2);
             particleSystem.colorDead = Color4.FromArray(parsedParticleSystem.colorDead);
@@ -1851,7 +1917,11 @@
                 for (var velocityGradient of parsedParticleSystem.velocityGradients) {
                     particleSystem.addVelocityGradient(velocityGradient.gradient, velocityGradient.factor1 !== undefined ?  velocityGradient.factor1 : velocityGradient.factor, velocityGradient.factor2);
                 }
-            }              
+            }     
+            
+            if (parsedParticleSystem.noiseTexture) {
+                particleSystem.noiseTexture = ProceduralTexture.Parse(parsedParticleSystem.noiseTexture, scene, rootUrl);
+            }
             
             // Emitter
             let emitterType: IParticleEmitterType;
@@ -1928,4 +1998,4 @@
             return particleSystem;
         }
     }
-}
+}

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

@@ -100,6 +100,10 @@ uniform sampler2D angularSpeedGradientSampler;
 uniform sampler2D velocityGradientSampler;
 #endif
 
+#ifdef NOISE
+uniform vec3 noiseStrength;
+uniform sampler2D noiseSampler;
+#endif
 
 #ifdef ANIMATESHEET
 uniform vec3 cellInfos;
@@ -267,7 +271,19 @@ void main() {
 #ifndef BILLBOARD    
     outInitialDirection = initialDirection;
 #endif
-    outDirection = direction;// + gravity * timeDelta;
+    outDirection = direction + gravity * timeDelta;
+
+#ifdef NOISE
+    vec3 localPosition = outPosition - emitterWM[3].xyz;
+
+    float fetchedR = texture(noiseSampler, vec2(localPosition.y, localPosition.z) * vec2(0.5) + vec2(0.5)).r;
+    float fetchedG = texture(noiseSampler, vec2(localPosition.x + 0.33, localPosition.z + 0.33) * vec2(0.5) + vec2(0.5)).r;
+    float fetchedB = texture(noiseSampler, vec2(localPosition.z - 0.33, localPosition.y - 0.33) * vec2(0.5) + vec2(0.5)).r;
+
+    vec3 force = vec3(2. * fetchedR - 1., 2. * fetchedG - 1., 2. * fetchedB - 1.) * noiseStrength;
+
+    outDirection = outDirection + force * timeDelta;
+#endif    
 
 #ifdef ANGULARSPEEDGRADIENTS
     float angularSpeed = texture(angularSpeedGradientSampler, vec2(ageGradient, 0)).r;

+ 23 - 37
src/Shaders/noise.fragment.fx

@@ -2,53 +2,39 @@
 
 // Uniforms
 uniform float brightness;
-uniform int firstOctave;
 uniform int octaves;
 uniform float persistence;
-uniform vec2 timeScale;
+uniform float timeScale;
 
 // Varyings
 varying vec2 vUV;
 
 // Functions
-float noise(int x,int y)
-{   
-    float fx = float(x);
-    float fy = float(y);
-    
-    return 2.0 * fract(sin(dot(vec2(fx, fy), vec2(12.9898, 78.233))) * 43758.5453) - 1.0;
-}
-
-float smoothNoise(int x,int y)
-{
-    return noise(x, y) / 4.0 + (noise(x + 1, y) + noise(x - 1,y) + 
-           noise(x, y + 1) + noise(x, y - 1)) / 8.0 + (noise(x + 1, y + 1) +
-           noise(x + 1,y - 1) + noise(x - 1, y + 1) + noise(x - 1,y - 1)) / 16.0;
-}
-
-float cosInterpolation(float x,float y,float n)
+vec2 hash22(vec2 p)
 {
-    float r = n * 3.1415926;
-    float f = (1.0 - cos(r)) * 0.5;
-    return x * (1.0 - f) + y * f;    
+    p = p * mat2(127.1, 311.7, 269.5, 183.3);
+	p = -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
+	return sin(p * 6.283 + timeScale);
 }
 
-float interpolationNoise(float x, float y)
+float interpolationNoise(vec2 p)
 {
-    int ix = int(x);
-    int iy = int(y);
-    float fracx = x - float(int(x));
-    float fracy = y - float(int(y));
+	vec2 pi = floor(p);
+    vec2 pf = p-pi;
     
-    float v1 = smoothNoise(ix, iy);
-    float v2 = smoothNoise(ix + 1, iy);
-    float v3 = smoothNoise(ix, iy + 1);
-    float v4 = smoothNoise(ix + 1, iy + 1);
+    vec2 w = pf * pf * (3.-2. * pf);
     
-   	float i1 = cosInterpolation(v1, v2, fracx);
-    float i2 = cosInterpolation(v3, v4, fracx);
+    float f00 = dot(hash22(pi + vec2(.0,.0)), pf-vec2(.0,.0));
+    float f01 = dot(hash22(pi + vec2(.0,1.)), pf-vec2(.0,1.));
+    float f10 = dot(hash22(pi + vec2(1.0,0.)), pf-vec2(1.0,0.));
+    float f11 = dot(hash22(pi + vec2(1.0,1.)), pf-vec2(1.0,1.));
+     
+    float xm1 = mix(f00,f10,w.x);
+    float xm2 = mix(f01,f11,w.x);
     
-    return cosInterpolation(i1, i2, fracy);    
+    float ym = mix(xm1,xm2,w.y); 
+    return ym;
+   
 }
 
 float perlinNoise2D(float x,float y)
@@ -56,11 +42,11 @@ float perlinNoise2D(float x,float y)
     float sum = 0.0;
     float frequency = 0.0;
     float amplitude = 0.0;
-    for(int i = firstOctave; i < octaves + firstOctave; i++)
+    for(int i = 0; i < octaves; i++)
     {
         frequency = pow(2.0, float(i));
         amplitude = pow(persistence, float(i));
-        sum = sum + interpolationNoise(x * frequency, y * frequency) * amplitude;
+        sum = sum + interpolationNoise(vec2(x * frequency, y * frequency)) * amplitude;
     }
     
     return sum;
@@ -69,8 +55,8 @@ float perlinNoise2D(float x,float y)
 // Main
 void main(void)
 {
-    float x = abs(vUV.x + timeScale.x);
-    float y = abs(vUV.y + timeScale.y);
+    float x = abs(vUV.x);
+    float y = abs(vUV.y);
 
     float noise = brightness + (1.0 - brightness) * perlinNoise2D(x,y);
 

+ 8 - 1
src/Tools/babylon.sceneSerializer.ts

@@ -67,10 +67,17 @@
             if (mesh.material) {
                 if (mesh.material instanceof MultiMaterial) {
                     serializationObject.multiMaterials = serializationObject.multiMaterials || [];
+                    serializationObject.materials = serializationObject.materials || [];
                     if (!serializationObject.multiMaterials.some((mat: Material) => (mat.id === (<Material>mesh.material).id))) {
                         serializationObject.multiMaterials.push(mesh.material.serialize());
+                        for (let submaterial of mesh.material.subMaterials) {
+                            if (submaterial) {
+                                if (!serializationObject.materials.some((mat: Material) => (mat.id === (<Material>submaterial).id))) {
+                                    serializationObject.materials.push(submaterial.serialize());
+                                }
+                            }
+                        }
                     }
-
                 } else {
                     serializationObject.materials = serializationObject.materials || [];
                     if (!serializationObject.materials.some((mat: Material) => (mat.id === (<Material>mesh.material).id))) {

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 20 - 0
src/Tools/babylon.tools.ts