فهرست منبع

Merge branch 'master' into EXT_lights_imageBased

David Catuhe 7 سال پیش
والد
کامیت
c13044bcfd
48فایلهای تغییر یافته به همراه21519 افزوده شده و 20208 حذف شده
  1. 10540 10416
      Playground/babylon.d.txt
  2. BIN
      Playground/scenes/Box/Box.png
  3. BIN
      Viewer/dist/babylon.woff
  4. 16 1
      Viewer/src/configuration/loader.ts
  5. 4 4
      Viewer/src/configuration/types/index.ts
  6. 4 3
      Viewer/src/templating/templateManager.ts
  7. 3 0
      Viewer/tests/commons/helper.ts
  8. 1 1
      Viewer/tests/unit/src/configuration/updateConfiguration.ts
  9. 8497 8374
      dist/preview release/babylon.d.ts
  10. 44 44
      dist/preview release/babylon.js
  11. 373 203
      dist/preview release/babylon.max.js
  12. 373 203
      dist/preview release/babylon.no-module.max.js
  13. 48 48
      dist/preview release/babylon.worker.js
  14. 373 203
      dist/preview release/es6.js
  15. 16 15
      dist/preview release/serializers/babylon.glTF2Serializer.d.ts
  16. 100 76
      dist/preview release/serializers/babylon.glTF2Serializer.js
  17. 2 2
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  18. 16 15
      dist/preview release/serializers/babylonjs.serializers.d.ts
  19. 100 76
      dist/preview release/serializers/babylonjs.serializers.js
  20. 2 2
      dist/preview release/serializers/babylonjs.serializers.min.js
  21. 16 15
      dist/preview release/serializers/babylonjs.serializers.module.d.ts
  22. 2 58
      dist/preview release/typedocValidationBaseline.json
  23. 4 0
      dist/preview release/viewer/babylon.viewer.d.ts
  24. 67 67
      dist/preview release/viewer/babylon.viewer.js
  25. 377 205
      dist/preview release/viewer/babylon.viewer.max.js
  26. 4 0
      dist/preview release/viewer/babylon.viewer.module.d.ts
  27. 3 0
      dist/preview release/what's new.md
  28. 1 1
      src/Culling/babylon.boundingSphere.ts
  29. 11 8
      src/Gizmos/babylon.axisDragGizmo.ts
  30. 11 8
      src/Gizmos/babylon.axisScaleGizmo.ts
  31. 7 4
      src/Gizmos/babylon.gizmo.ts
  32. 12 9
      src/Gizmos/babylon.planeRotationGizmo.ts
  33. 27 18
      src/Gizmos/babylon.positionGizmo.ts
  34. 27 18
      src/Gizmos/babylon.rotationGizmo.ts
  35. 27 18
      src/Gizmos/babylon.scaleGizmo.ts
  36. 19 2
      src/Layer/babylon.effectLayer.ts
  37. 21 4
      src/Layer/babylon.effectLayerSceneComponent.ts
  38. 12 1
      src/Layer/babylon.glowLayer.ts
  39. 16 1
      src/Layer/babylon.highlightLayer.ts
  40. 24 0
      src/Particles/babylon.IParticleSystem.ts
  41. 158 71
      src/Particles/babylon.gpuParticleSystem.ts
  42. 13 1
      src/Particles/babylon.particle.ts
  43. 92 4
      src/Particles/babylon.particleSystem.ts
  44. 8 1
      src/Rendering/babylon.renderingManager.ts
  45. 7 7
      src/Shaders/gpuRenderParticles.vertex.fx
  46. 23 0
      src/Shaders/gpuUpdateParticles.vertex.fx
  47. 11 1
      src/babylon.scene.ts
  48. 7 0
      src/babylon.sceneComponent.ts

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


BIN
Playground/scenes/Box/Box.png


BIN
Viewer/dist/babylon.woff


+ 16 - 1
Viewer/src/configuration/loader.ts

@@ -33,8 +33,9 @@ export class ConfigurationLoader {
     public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
 
         let loadedConfig: ViewerConfiguration = deepmerge({}, initConfig);
+        this._processInitialConfiguration(loadedConfig);
 
-        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "");
+        let extendedConfiguration = getConfigurationType(loadedConfig.extends || "extended");
 
         if (loadedConfig.configuration) {
 
@@ -94,6 +95,20 @@ export class ConfigurationLoader {
         this._loadRequests.length = 0;
     }
 
+    /**
+     * This function will process the initial configuration and make needed changes for the viewer to work.
+     * @param config the mutable(!) initial configuration to process
+     */
+    private _processInitialConfiguration(config: ViewerConfiguration) {
+        if (config.model) {
+            if (typeof config.model === "string") {
+                config.model = {
+                    url: config.model
+                }
+            }
+        }
+    }
+
     private _loadFile(url: string): Promise<any> {
         let cacheReference = this._configurationCache;
         if (this._enableCache && cacheReference[url]) {

+ 4 - 4
Viewer/src/configuration/types/index.ts

@@ -28,17 +28,17 @@ let getConfigurationType = function (types: string): ViewerConfiguration {
             case 'shadowSpotLight':
                 config = deepmerge(config, shadowSpotlLightConfiguration);
                 break;
-            case 'extended':
-                config = deepmerge(config, extendedConfiguration);
+            case 'default':
+                config = deepmerge(config, defaultConfiguration);
                 break;
             case 'minimal':
                 config = deepmerge(config, minimalConfiguration);
                 break;
             case 'none':
                 break;
-            case 'default':
+            case 'extended':
             default:
-                config = deepmerge(config, defaultConfiguration);
+                config = deepmerge(config, extendedConfiguration);
                 break;
         }
 

+ 4 - 3
Viewer/src/templating/templateManager.ts

@@ -80,8 +80,9 @@ export class TemplateManager {
             // register the observers
             //template.onLoaded.add(() => {
             let addToParent = () => {
-                let containingElement = parentTemplate && parentTemplate.parent.querySelector(camelToKebab(name)) || this.containerElement;
-                template.appendTo(containingElement);
+                let lastElements = parentTemplate && parentTemplate.parent.querySelectorAll(camelToKebab(name));
+                let containingElement = (lastElements && lastElements.length && lastElements.item(lastElements.length - 1)) || this.containerElement;
+                template.appendTo(<HTMLElement>containingElement);
                 this._checkLoadedState();
             }
 
@@ -543,7 +544,7 @@ export class Template {
     private _getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
         if (!templateConfig) {
             return Promise.reject('No templateConfig provided');
-        } else if (templateConfig.html) {
+        } else if (templateConfig.html && !templateConfig.location) {
             return Promise.resolve(templateConfig.html);
         } else {
             let location = this._getTemplateLocation(templateConfig);

+ 3 - 0
Viewer/tests/commons/helper.ts

@@ -8,6 +8,9 @@ export const useNullEngine = true;
 export class Helper {
 
     public static getNewViewerInstance(element: HTMLElement = Helper.getViewerContainer(), configuration?: ViewerConfiguration, useAbstractViewer?: boolean) {
+        if (configuration) {
+            configuration.extends = configuration.extends || "default";
+        }
         if (useNullEngine) {
             if (useAbstractViewer) {
                 return new NullEngineAbstractViewer(element, configuration);

+ 1 - 1
Viewer/tests/unit/src/configuration/updateConfiguration.ts

@@ -36,7 +36,7 @@ describe(name + " scene", () => {
             }
         })
         viewer.onInitDoneObservable.add(() => {
-            assert.isUndefined(viewer.configuration.scene);
+            // assert.isUndefined(viewer.configuration.scene);
             assert.equal(showCalled, 0);
             assert.equal(hideCalled, 0);
             viewer.updateConfiguration({

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


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


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


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


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


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


+ 16 - 15
dist/preview release/serializers/babylon.glTF2Serializer.d.ts

@@ -560,7 +560,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -575,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -804,7 +804,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -817,7 +817,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -825,7 +825,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -834,52 +834,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 100 - 76
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -367,12 +367,26 @@ var BABYLON;
                     var vertex = vertices_1[_i];
                     if (this._convertToRightHandedSystem && !(vertexAttributeKind === BABYLON.VertexBuffer.ColorKind) && !(vertex instanceof BABYLON.Vector2)) {
                         if (vertex instanceof BABYLON.Vector3) {
-                            (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                            if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                            }
+                            else if (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                            }
+                            else {
+                                BABYLON.Tools.Error('Unsupported vertex attribute kind!');
+                            }
                         }
                         else {
-                            GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                            GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertex);
                         }
                     }
+                    if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                        vertex.normalize();
+                    }
+                    else if (vertexAttributeKind === BABYLON.VertexBuffer.TangentKind && vertex instanceof BABYLON.Vector4) {
+                        GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertex);
+                    }
                     for (var _a = 0, _b = vertex.asArray(); _a < _b.length; _a++) {
                         var component = _b[_a];
                         binaryWriter.setFloat32(component, byteOffset);
@@ -398,7 +412,7 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                             }
                             vertexAttributes.push(vertexData.asArray());
                         }
@@ -409,8 +423,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                             }
+                            vertexData.normalize();
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -420,8 +435,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector4.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                             }
+                            GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertexData);
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -513,7 +529,7 @@ var BABYLON;
                             if (image.uri) {
                                 imageData = _this._imageData[image.uri];
                                 imageName = image.uri.split('.')[0] + " image";
-                                bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                                bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 _this._bufferViews.push(bufferView);
                                 image.bufferView = _this._bufferViews.length - 1;
@@ -663,7 +679,7 @@ var BABYLON;
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
                 if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
                 }
                 if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonTransformNode.scaling.asArray();
@@ -674,7 +690,7 @@ var BABYLON;
                 }
                 if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                     if (this._convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                     }
                     node.rotation = rotationQuaternion.normalize().asArray();
                 }
@@ -702,7 +718,7 @@ var BABYLON;
                     var vertexData = bufferMesh.getVerticesData(kind);
                     if (vertexData) {
                         var byteLength = vertexData.length * 4;
-                        var bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                        var bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
                     }
@@ -843,7 +859,7 @@ var BABYLON;
                         var indices = bufferMesh.getIndices();
                         if (indices) {
                             var byteLength = indices.length * 4;
-                            bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                            bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                             this._bufferViews.push(bufferView);
                             indexBufferViewIndex = this._bufferViews.length - 1;
                             for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
@@ -902,9 +918,9 @@ var BABYLON;
                                         if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                             minMax = { min: null, max: null };
                                             if (attributeKind == BABYLON.VertexBuffer.PositionKind) {
-                                                minMax = GLTF2._GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                                minMax = GLTF2._GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                             }
-                                            var accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                            var accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                             this._accessors.push(accessor);
                                             this.setAttributeKind(meshPrimitive, attributeKind);
                                             if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -916,7 +932,7 @@ var BABYLON;
                             }
                             if (indexBufferViewIndex) {
                                 // Create accessor
-                                var accessor = GLTF2._GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
+                                var accessor = GLTF2._GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
                                 this._accessors.push(accessor);
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
@@ -1970,7 +1986,7 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._gonvertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var promises = [];
                 var metallicRoughness = {
                     baseColor: babylonPBRMaterial.albedoColor,
@@ -2102,39 +2118,41 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughness = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var _this = this;
-                var samplers = this._exporter._samplers;
-                var textures = this._exporter._textures;
-                var specGloss = {
-                    diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
-                    specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
-                    glossiness: babylonPBRMaterial.microSurface || 1,
-                };
-                var samplerIndex = null;
-                var sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                    samplers.push(sampler);
-                    samplerIndex = samplers.length - 1;
-                }
-                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-                }
-                return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
-                    if (hasTextureCoords) {
-                        if (metallicRoughnessFactors.baseColorTextureBase64) {
-                            var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFBaseColorTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                return Promise.resolve().then(function () {
+                    var samplers = _this._exporter._samplers;
+                    var textures = _this._exporter._textures;
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    var samplerIndex = null;
+                    var sampler = _this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                    if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                        samplers.push(sampler);
+                        samplerIndex = samplers.length - 1;
+                    }
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                    }
+                    if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                        return _this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
-                        }
-                        if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                            var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFMRColorTexture) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
-                        }
-                        return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                        });
                     }
                     else {
                         return _this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -2167,12 +2185,12 @@ var BABYLON;
                             babylonPBRMaterial.alpha
                         ];
                     }
-                    return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
                 else {
-                    return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
@@ -2598,26 +2616,26 @@ var BABYLON;
                     var nodeIndex = nodeMap[babylonTransformNode.uniqueId];
                     // Creates buffer view and accessor for key frames.
                     var byteLength = animationData.inputs.length * 4;
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
                     bufferViews.push(bufferView);
                     animationData.inputs.forEach(function (input) {
                         binaryWriter.setFloat32(input);
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                     accessors.push(accessor);
                     keyframeAccessorIndex = accessors.length - 1;
                     // create bufferview and accessor for keyed values.
                     outputLength = animationData.outputs.length;
                     byteLength = dataAccessorType === "VEC3" /* VEC3 */ ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
                     // check for in and out tangents
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
                     bufferViews.push(bufferView);
                     animationData.outputs.forEach(function (output) {
                         output.forEach(function (entry) {
                             binaryWriter.setFloat32(entry);
                         });
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
                     accessors.push(accessor);
                     dataAccessorIndex = accessors.length - 1;
                     // create sampler
@@ -2757,7 +2775,7 @@ var BABYLON;
                             BABYLON.Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                         }
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
                             if (!babylonTransformNode.parent) {
                                 quaternionCache = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
                             }
@@ -2767,7 +2785,7 @@ var BABYLON;
                     else {
                         cacheValue = value;
                         if (convertToRightHandedSystem && (animationChannelTargetPath !== "scale" /* SCALE */)) {
-                            GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                            GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                             if (!babylonTransformNode.parent) {
                                 cacheValue.x *= -1;
                                 cacheValue.z *= -1;
@@ -2822,7 +2840,7 @@ var BABYLON;
                         if (babylonTransformNode.rotationQuaternion) {
                             basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     basePositionRotationOrScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                                 }
@@ -2834,13 +2852,13 @@ var BABYLON;
                     }
                     else {
                         basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                        GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                     basePositionRotationOrScale = babylonTransformNode.position.asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else { // scale
@@ -2868,7 +2886,7 @@ var BABYLON;
                         var array = BABYLON.Vector3.FromArray(value);
                         var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                             if (!babylonTransformNode.parent) {
                                 rotationQuaternion = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
                             }
@@ -2877,7 +2895,7 @@ var BABYLON;
                     }
                     else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                            GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                             if (!babylonTransformNode.parent) {
                                 value[0] *= -1;
                                 value[2] *= -1;
@@ -2892,7 +2910,7 @@ var BABYLON;
                         if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                             var posRotScale = useQuaternion ? newPositionRotationOrScale : BABYLON.Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
                                 if (!babylonTransformNode.parent) {
                                     posRotScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
                                 }
@@ -2901,7 +2919,7 @@ var BABYLON;
                         }
                         else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     newPositionRotationOrScale.x *= -1;
                                     newPositionRotationOrScale.z *= -1;
@@ -2914,7 +2932,7 @@ var BABYLON;
                 else if (animationType === BABYLON.Animation.ANIMATIONTYPE_QUATERNION) {
                     value = keyFrame.value.normalize().asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
                         if (!babylonTransformNode.parent) {
                             value = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(value)).asArray();
                         }
@@ -3002,7 +3020,7 @@ var BABYLON;
                                 tangent = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                             }
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(tangent)).asArray();
                                 }
@@ -3017,7 +3035,7 @@ var BABYLON;
                             tangent = tangentValue.scale(frameDelta).asArray();
                             if (convertToRightHandedSystem) {
                                 if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
-                                    GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                    GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                     if (!babylonTransformNode.parent) {
                                         tangent[0] *= -1; // x
                                         tangent[2] *= -1; // z
@@ -3074,7 +3092,7 @@ var BABYLON;
              * @param name name of the buffer view
              * @returns bufferView for glTF
              */
-            _GLTFUtilities.CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
+            _GLTFUtilities._CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset) {
                     bufferview.byteOffset = byteOffset;
@@ -3099,7 +3117,7 @@ var BABYLON;
              * @param max Maximum value of each component in this attribute
              * @returns accessor for glTF
              */
-            _GLTFUtilities.CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
+            _GLTFUtilities._CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min != null) {
                     accessor.min = min;
@@ -3119,7 +3137,7 @@ var BABYLON;
              * @param vertexCount Number of vertices to check for min and max values
              * @returns min number array and max number array
              */
-            _GLTFUtilities.CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
+            _GLTFUtilities._CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var positionStrideSize = 3;
@@ -3131,7 +3149,7 @@ var BABYLON;
                         indexOffset = positionStrideSize * i;
                         position = BABYLON.Vector3.FromArray(positions, indexOffset);
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                         }
                         vector = position.asArray();
                         for (var j = 0; j < positionStrideSize; ++j) {
@@ -3153,21 +3171,21 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedPositionVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
@@ -3175,28 +3193,28 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedNormalVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedVector4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedVector4FromRef = function (vector) {
                 vector.z *= -1;
                 vector.w *= -1;
             };
@@ -3204,7 +3222,7 @@ var BABYLON;
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedArray4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedArray4FromRef = function (vector) {
                 vector[2] *= -1;
                 vector[3] *= -1;
             };
@@ -3212,7 +3230,7 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionFromRef = function (quaternion) {
                 quaternion.x *= -1;
                 quaternion.y *= -1;
             };
@@ -3220,10 +3238,16 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef = function (quaternion) {
                 quaternion[0] *= -1;
                 quaternion[1] *= -1;
             };
+            _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            };
             return _GLTFUtilities;
         }());
         GLTF2._GLTFUtilities = _GLTFUtilities;

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


+ 16 - 15
dist/preview release/serializers/babylonjs.serializers.d.ts

@@ -568,7 +568,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -583,7 +583,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -812,7 +812,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -825,7 +825,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -833,7 +833,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -842,52 +842,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 100 - 76
dist/preview release/serializers/babylonjs.serializers.js

@@ -517,12 +517,26 @@ var BABYLON;
                     var vertex = vertices_1[_i];
                     if (this._convertToRightHandedSystem && !(vertexAttributeKind === BABYLON.VertexBuffer.ColorKind) && !(vertex instanceof BABYLON.Vector2)) {
                         if (vertex instanceof BABYLON.Vector3) {
-                            (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertex) : GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertex);
+                            if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertex);
+                            }
+                            else if (vertexAttributeKind === BABYLON.VertexBuffer.PositionKind) {
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertex);
+                            }
+                            else {
+                                BABYLON.Tools.Error('Unsupported vertex attribute kind!');
+                            }
                         }
                         else {
-                            GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertex);
+                            GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertex);
                         }
                     }
+                    if (vertexAttributeKind === BABYLON.VertexBuffer.NormalKind) {
+                        vertex.normalize();
+                    }
+                    else if (vertexAttributeKind === BABYLON.VertexBuffer.TangentKind && vertex instanceof BABYLON.Vector4) {
+                        GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertex);
+                    }
                     for (var _a = 0, _b = vertex.asArray(); _a < _b.length; _a++) {
                         var component = _b[_a];
                         binaryWriter.setFloat32(component, byteOffset);
@@ -548,7 +562,7 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(vertexData);
                             }
                             vertexAttributes.push(vertexData.asArray());
                         }
@@ -559,8 +573,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector3.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(vertexData);
                             }
+                            vertexData.normalize();
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -570,8 +585,9 @@ var BABYLON;
                             index = k * stride;
                             var vertexData = BABYLON.Vector4.FromArray(meshAttributeArray, index);
                             if (this._convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedVector4FromRef(vertexData);
+                                GLTF2._GLTFUtilities._GetRightHandedVector4FromRef(vertexData);
                             }
+                            GLTF2._GLTFUtilities._NormalizeTangentFromRef(vertexData);
                             vertexAttributes.push(vertexData.asArray());
                         }
                         break;
@@ -663,7 +679,7 @@ var BABYLON;
                             if (image.uri) {
                                 imageData = _this._imageData[image.uri];
                                 imageName = image.uri.split('.')[0] + " image";
-                                bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
+                                bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, byteOffset, imageData.data.length, undefined, imageName);
                                 byteOffset += imageData.data.buffer.byteLength;
                                 _this._bufferViews.push(bufferView);
                                 image.bufferView = _this._bufferViews.length - 1;
@@ -813,7 +829,7 @@ var BABYLON;
              */
             _Exporter.prototype.setNodeTransformation = function (node, babylonTransformNode) {
                 if (!babylonTransformNode.position.equalsToFloats(0, 0, 0)) {
-                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities.GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
+                    node.translation = this._convertToRightHandedSystem ? GLTF2._GLTFUtilities._GetRightHandedPositionVector3(babylonTransformNode.position).asArray() : babylonTransformNode.position.asArray();
                 }
                 if (!babylonTransformNode.scaling.equalsToFloats(1, 1, 1)) {
                     node.scale = babylonTransformNode.scaling.asArray();
@@ -824,7 +840,7 @@ var BABYLON;
                 }
                 if (!(rotationQuaternion.x === 0 && rotationQuaternion.y === 0 && rotationQuaternion.z === 0 && rotationQuaternion.w === 1)) {
                     if (this._convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                     }
                     node.rotation = rotationQuaternion.normalize().asArray();
                 }
@@ -852,7 +868,7 @@ var BABYLON;
                     var vertexData = bufferMesh.getVerticesData(kind);
                     if (vertexData) {
                         var byteLength = vertexData.length * 4;
-                        var bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
+                        var bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, byteStride, kind + " - " + bufferMesh.name);
                         this._bufferViews.push(bufferView);
                         this.writeAttributeData(kind, vertexData, byteStride, binaryWriter);
                     }
@@ -993,7 +1009,7 @@ var BABYLON;
                         var indices = bufferMesh.getIndices();
                         if (indices) {
                             var byteLength = indices.length * 4;
-                            bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
+                            bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, "Indices - " + bufferMesh.name);
                             this._bufferViews.push(bufferView);
                             indexBufferViewIndex = this._bufferViews.length - 1;
                             for (var k = 0, length_7 = indices.length; k < length_7; ++k) {
@@ -1052,9 +1068,9 @@ var BABYLON;
                                         if (bufferViewIndex != undefined) { // check to see if bufferviewindex has a numeric value assigned.
                                             minMax = { min: null, max: null };
                                             if (attributeKind == BABYLON.VertexBuffer.PositionKind) {
-                                                minMax = GLTF2._GLTFUtilities.CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
+                                                minMax = GLTF2._GLTFUtilities._CalculateMinMaxPositions(vertexData, 0, vertexData.length / stride, this._convertToRightHandedSystem);
                                             }
-                                            var accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
+                                            var accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViewIndex, attributeKind + " - " + babylonTransformNode.name, attribute.accessorType, 5126 /* FLOAT */, vertexData.length / stride, 0, minMax.min, minMax.max);
                                             this._accessors.push(accessor);
                                             this.setAttributeKind(meshPrimitive, attributeKind);
                                             if (meshPrimitive.attributes.TEXCOORD_0 != null || meshPrimitive.attributes.TEXCOORD_1 != null) {
@@ -1066,7 +1082,7 @@ var BABYLON;
                             }
                             if (indexBufferViewIndex) {
                                 // Create accessor
-                                var accessor = GLTF2._GLTFUtilities.CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
+                                var accessor = GLTF2._GLTFUtilities._CreateAccessor(indexBufferViewIndex, "indices - " + babylonTransformNode.name, "SCALAR" /* SCALAR */, 5125 /* UNSIGNED_INT */, submesh.indexCount, submesh.indexStart * 4, null, null);
                                 this._accessors.push(accessor);
                                 meshPrimitive.indices = this._accessors.length - 1;
                             }
@@ -2120,7 +2136,7 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._gonvertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertMetalRoughFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var promises = [];
                 var metallicRoughness = {
                     baseColor: babylonPBRMaterial.albedoColor,
@@ -2252,39 +2268,41 @@ var BABYLON;
              * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
              * @returns glTF PBR Metallic Roughness factors
              */
-            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughness = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
+            _GLTFMaterialExporter.prototype._convertSpecGlossFactorsToMetallicRoughnessAsync = function (babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords) {
                 var _this = this;
-                var samplers = this._exporter._samplers;
-                var textures = this._exporter._textures;
-                var specGloss = {
-                    diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
-                    specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
-                    glossiness: babylonPBRMaterial.microSurface || 1,
-                };
-                var samplerIndex = null;
-                var sampler = this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
-                if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
-                    samplers.push(sampler);
-                    samplerIndex = samplers.length - 1;
-                }
-                if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
-                    return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
-                }
-                return this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
-                    if (hasTextureCoords) {
-                        if (metallicRoughnessFactors.baseColorTextureBase64) {
-                            var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFBaseColorTexture) {
-                                glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                return Promise.resolve().then(function () {
+                    var samplers = _this._exporter._samplers;
+                    var textures = _this._exporter._textures;
+                    var specGloss = {
+                        diffuseColor: babylonPBRMaterial.albedoColor || BABYLON.Color3.White(),
+                        specularColor: babylonPBRMaterial.reflectivityColor || BABYLON.Color3.White(),
+                        glossiness: babylonPBRMaterial.microSurface || 1,
+                    };
+                    var samplerIndex = null;
+                    var sampler = _this._getGLTFTextureSampler(babylonPBRMaterial.albedoTexture);
+                    if (sampler.magFilter != null && sampler.minFilter != null && sampler.wrapS != null && sampler.wrapT != null) {
+                        samplers.push(sampler);
+                        samplerIndex = samplers.length - 1;
+                    }
+                    if (babylonPBRMaterial.reflectivityTexture && !babylonPBRMaterial.useMicroSurfaceFromReflectivityMapAlpha) {
+                        return Promise.reject("_ConvertPBRMaterial: Glossiness values not included in the reflectivity texture are currently not supported");
+                    }
+                    if ((babylonPBRMaterial.albedoTexture || babylonPBRMaterial.reflectivityTexture) && hasTextureCoords) {
+                        return _this._convertSpecularGlossinessTexturesToMetallicRoughnessAsync(babylonPBRMaterial.albedoTexture, babylonPBRMaterial.reflectivityTexture, specGloss, mimeType).then(function (metallicRoughnessFactors) {
+                            if (metallicRoughnessFactors.baseColorTextureBase64) {
+                                var glTFBaseColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.baseColorTextureBase64, "bjsBaseColorTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.albedoTexture ? babylonPBRMaterial.albedoTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFBaseColorTexture) {
+                                    glTFPbrMetallicRoughness.baseColorTexture = glTFBaseColorTexture;
+                                }
                             }
-                        }
-                        if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
-                            var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
-                            if (glTFMRColorTexture) {
-                                glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                            if (metallicRoughnessFactors.metallicRoughnessTextureBase64) {
+                                var glTFMRColorTexture = _this._getTextureInfoFromBase64(metallicRoughnessFactors.metallicRoughnessTextureBase64, "bjsMetallicRoughnessTexture_" + (textures.length) + ".png", mimeType, babylonPBRMaterial.reflectivityTexture ? babylonPBRMaterial.reflectivityTexture.coordinatesIndex : null, samplerIndex);
+                                if (glTFMRColorTexture) {
+                                    glTFPbrMetallicRoughness.metallicRoughnessTexture = glTFMRColorTexture;
+                                }
                             }
-                        }
-                        return metallicRoughnessFactors;
+                            return metallicRoughnessFactors;
+                        });
                     }
                     else {
                         return _this._convertSpecularGlossinessToMetallicRoughness(specGloss);
@@ -2317,12 +2335,12 @@ var BABYLON;
                             babylonPBRMaterial.alpha
                         ];
                     }
-                    return this._gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
                 else {
-                    return this._convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
+                    return this._convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords).then(function (metallicRoughness) {
                         return _this.setMetallicRoughnessPbrMaterial(metallicRoughness, babylonPBRMaterial, glTFMaterial, glTFPbrMetallicRoughness, mimeType, hasTextureCoords);
                     });
                 }
@@ -2748,26 +2766,26 @@ var BABYLON;
                     var nodeIndex = nodeMap[babylonTransformNode.uniqueId];
                     // Creates buffer view and accessor for key frames.
                     var byteLength = animationData.inputs.length * 4;
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  keyframe data view");
                     bufferViews.push(bufferView);
                     animationData.inputs.forEach(function (input) {
                         binaryWriter.setFloat32(input);
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  keyframes", "SCALAR" /* SCALAR */, 5126 /* FLOAT */, animationData.inputs.length, null, [animationData.inputsMin], [animationData.inputsMax]);
                     accessors.push(accessor);
                     keyframeAccessorIndex = accessors.length - 1;
                     // create bufferview and accessor for keyed values.
                     outputLength = animationData.outputs.length;
                     byteLength = dataAccessorType === "VEC3" /* VEC3 */ ? animationData.outputs.length * 12 : animationData.outputs.length * 16;
                     // check for in and out tangents
-                    bufferView = GLTF2._GLTFUtilities.CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
+                    bufferView = GLTF2._GLTFUtilities._CreateBufferView(0, binaryWriter.getByteOffset(), byteLength, undefined, name + "  data view");
                     bufferViews.push(bufferView);
                     animationData.outputs.forEach(function (output) {
                         output.forEach(function (entry) {
                             binaryWriter.setFloat32(entry);
                         });
                     });
-                    accessor = GLTF2._GLTFUtilities.CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
+                    accessor = GLTF2._GLTFUtilities._CreateAccessor(bufferViews.length - 1, name + "  data", dataAccessorType, 5126 /* FLOAT */, outputLength, null, null, null);
                     accessors.push(accessor);
                     dataAccessorIndex = accessors.length - 1;
                     // create sampler
@@ -2907,7 +2925,7 @@ var BABYLON;
                             BABYLON.Quaternion.RotationYawPitchRollToRef(cacheValue.y, cacheValue.x, cacheValue.z, quaternionCache);
                         }
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(quaternionCache);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(quaternionCache);
                             if (!babylonTransformNode.parent) {
                                 quaternionCache = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(quaternionCache);
                             }
@@ -2917,7 +2935,7 @@ var BABYLON;
                     else {
                         cacheValue = value;
                         if (convertToRightHandedSystem && (animationChannelTargetPath !== "scale" /* SCALE */)) {
-                            GLTF2._GLTFUtilities.GetRightHandedPositionVector3FromRef(cacheValue);
+                            GLTF2._GLTFUtilities._GetRightHandedPositionVector3FromRef(cacheValue);
                             if (!babylonTransformNode.parent) {
                                 cacheValue.x *= -1;
                                 cacheValue.z *= -1;
@@ -2972,7 +2990,7 @@ var BABYLON;
                         if (babylonTransformNode.rotationQuaternion) {
                             basePositionRotationOrScale = babylonTransformNode.rotationQuaternion.asArray();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(basePositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     basePositionRotationOrScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(basePositionRotationOrScale)).asArray();
                                 }
@@ -2984,13 +3002,13 @@ var BABYLON;
                     }
                     else {
                         basePositionRotationOrScale = babylonTransformNode.rotation.asArray();
-                        GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                     basePositionRotationOrScale = babylonTransformNode.position.asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
+                        GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(basePositionRotationOrScale);
                     }
                 }
                 else { // scale
@@ -3018,7 +3036,7 @@ var BABYLON;
                         var array = BABYLON.Vector3.FromArray(value);
                         var rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z);
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(rotationQuaternion);
+                            GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(rotationQuaternion);
                             if (!babylonTransformNode.parent) {
                                 rotationQuaternion = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(rotationQuaternion);
                             }
@@ -3027,7 +3045,7 @@ var BABYLON;
                     }
                     else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                         if (convertToRightHandedSystem) {
-                            GLTF2._GLTFUtilities.GetRightHandedNormalArray3FromRef(value);
+                            GLTF2._GLTFUtilities._GetRightHandedNormalArray3FromRef(value);
                             if (!babylonTransformNode.parent) {
                                 value[0] *= -1;
                                 value[2] *= -1;
@@ -3042,7 +3060,7 @@ var BABYLON;
                         if (animationChannelTargetPath === "rotation" /* ROTATION */) {
                             var posRotScale = useQuaternion ? newPositionRotationOrScale : BABYLON.Quaternion.RotationYawPitchRoll(newPositionRotationOrScale.y, newPositionRotationOrScale.x, newPositionRotationOrScale.z).normalize();
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionFromRef(posRotScale);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionFromRef(posRotScale);
                                 if (!babylonTransformNode.parent) {
                                     posRotScale = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(posRotScale);
                                 }
@@ -3051,7 +3069,7 @@ var BABYLON;
                         }
                         else if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
+                                GLTF2._GLTFUtilities._GetRightHandedNormalVector3FromRef(newPositionRotationOrScale);
                                 if (!babylonTransformNode.parent) {
                                     newPositionRotationOrScale.x *= -1;
                                     newPositionRotationOrScale.z *= -1;
@@ -3064,7 +3082,7 @@ var BABYLON;
                 else if (animationType === BABYLON.Animation.ANIMATIONTYPE_QUATERNION) {
                     value = keyFrame.value.normalize().asArray();
                     if (convertToRightHandedSystem) {
-                        GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(value);
+                        GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(value);
                         if (!babylonTransformNode.parent) {
                             value = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(value)).asArray();
                         }
@@ -3152,7 +3170,7 @@ var BABYLON;
                                 tangent = BABYLON.Quaternion.RotationYawPitchRoll(array.y, array.x, array.z).asArray();
                             }
                             if (convertToRightHandedSystem) {
-                                GLTF2._GLTFUtilities.GetRightHandedQuaternionArrayFromRef(tangent);
+                                GLTF2._GLTFUtilities._GetRightHandedQuaternionArrayFromRef(tangent);
                                 if (!babylonTransformNode.parent) {
                                     tangent = BABYLON.Quaternion.FromArray([0, 1, 0, 0]).multiply(BABYLON.Quaternion.FromArray(tangent)).asArray();
                                 }
@@ -3167,7 +3185,7 @@ var BABYLON;
                             tangent = tangentValue.scale(frameDelta).asArray();
                             if (convertToRightHandedSystem) {
                                 if (animationChannelTargetPath === "translation" /* TRANSLATION */) {
-                                    GLTF2._GLTFUtilities.GetRightHandedPositionArray3FromRef(tangent);
+                                    GLTF2._GLTFUtilities._GetRightHandedPositionArray3FromRef(tangent);
                                     if (!babylonTransformNode.parent) {
                                         tangent[0] *= -1; // x
                                         tangent[2] *= -1; // z
@@ -3224,7 +3242,7 @@ var BABYLON;
              * @param name name of the buffer view
              * @returns bufferView for glTF
              */
-            _GLTFUtilities.CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
+            _GLTFUtilities._CreateBufferView = function (bufferIndex, byteOffset, byteLength, byteStride, name) {
                 var bufferview = { buffer: bufferIndex, byteLength: byteLength };
                 if (byteOffset) {
                     bufferview.byteOffset = byteOffset;
@@ -3249,7 +3267,7 @@ var BABYLON;
              * @param max Maximum value of each component in this attribute
              * @returns accessor for glTF
              */
-            _GLTFUtilities.CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
+            _GLTFUtilities._CreateAccessor = function (bufferviewIndex, name, type, componentType, count, byteOffset, min, max) {
                 var accessor = { name: name, bufferView: bufferviewIndex, componentType: componentType, count: count, type: type };
                 if (min != null) {
                     accessor.min = min;
@@ -3269,7 +3287,7 @@ var BABYLON;
              * @param vertexCount Number of vertices to check for min and max values
              * @returns min number array and max number array
              */
-            _GLTFUtilities.CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
+            _GLTFUtilities._CalculateMinMaxPositions = function (positions, vertexStart, vertexCount, convertToRightHandedSystem) {
                 var min = [Infinity, Infinity, Infinity];
                 var max = [-Infinity, -Infinity, -Infinity];
                 var positionStrideSize = 3;
@@ -3281,7 +3299,7 @@ var BABYLON;
                         indexOffset = positionStrideSize * i;
                         position = BABYLON.Vector3.FromArray(positions, indexOffset);
                         if (convertToRightHandedSystem) {
-                            _GLTFUtilities.GetRightHandedPositionVector3FromRef(position);
+                            _GLTFUtilities._GetRightHandedPositionVector3FromRef(position);
                         }
                         vector = position.asArray();
                         for (var j = 0; j < positionStrideSize; ++j) {
@@ -3303,21 +3321,21 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedPositionVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedPositionArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedPositionArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
@@ -3325,28 +3343,28 @@ var BABYLON;
              * @param vector vector3 array
              * @returns right-handed Vector3
              */
-            _GLTFUtilities.GetRightHandedNormalVector3 = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3 = function (vector) {
                 return new BABYLON.Vector3(vector.x, vector.y, -vector.z);
             };
             /**
              * Converts a Vector3 to right-handed
              * @param vector Vector3 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalVector3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalVector3FromRef = function (vector) {
                 vector.z *= -1;
             };
             /**
              * Converts a three element number array to right-handed
              * @param vector number array to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedNormalArray3FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedNormalArray3FromRef = function (vector) {
                 vector[2] *= -1;
             };
             /**
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedVector4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedVector4FromRef = function (vector) {
                 vector.z *= -1;
                 vector.w *= -1;
             };
@@ -3354,7 +3372,7 @@ var BABYLON;
              * Converts a Vector4 to right-handed
              * @param vector Vector4 to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedArray4FromRef = function (vector) {
+            _GLTFUtilities._GetRightHandedArray4FromRef = function (vector) {
                 vector[2] *= -1;
                 vector[3] *= -1;
             };
@@ -3362,7 +3380,7 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionFromRef = function (quaternion) {
                 quaternion.x *= -1;
                 quaternion.y *= -1;
             };
@@ -3370,10 +3388,16 @@ var BABYLON;
              * Converts a Quaternion to right-handed
              * @param quaternion Source quaternion to convert to right-handed
              */
-            _GLTFUtilities.GetRightHandedQuaternionArrayFromRef = function (quaternion) {
+            _GLTFUtilities._GetRightHandedQuaternionArrayFromRef = function (quaternion) {
                 quaternion[0] *= -1;
                 quaternion[1] *= -1;
             };
+            _GLTFUtilities._NormalizeTangentFromRef = function (tangent) {
+                var length = Math.sqrt(tangent.x * tangent.x + tangent.y * tangent.y + tangent.z + tangent.z);
+                tangent.x /= length;
+                tangent.y /= length;
+                tangent.z /= length;
+            };
             return _GLTFUtilities;
         }());
         GLTF2._GLTFUtilities = _GLTFUtilities;

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


+ 16 - 15
dist/preview release/serializers/babylonjs.serializers.module.d.ts

@@ -575,7 +575,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _gonvertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertMetalRoughFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         private _getGLTFTextureSampler(texture);
         private _getGLTFTextureWrapMode(wrapMode);
         private _getGLTFTextureWrapModesSampler(texture);
@@ -590,7 +590,7 @@ declare module BABYLON.GLTF2 {
          * @param hasTextureCoords specifies if texture coordinates are present on the submesh to determine if textures should be applied
          * @returns glTF PBR Metallic Roughness factors
          */
-        private _convertSpecGlossFactorsToMetallicRoughness(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
+        private _convertSpecGlossFactorsToMetallicRoughnessAsync(babylonPBRMaterial, mimeType, glTFPbrMetallicRoughness, hasTextureCoords);
         /**
          * Converts a Babylon PBR Metallic Roughness Material to a glTF Material
          * @param babylonPBRMaterial BJS PBR Metallic Roughness Material
@@ -819,7 +819,7 @@ declare module BABYLON.GLTF2 {
          * @param name name of the buffer view
          * @returns bufferView for glTF
          */
-        static CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
+        static _CreateBufferView(bufferIndex: number, byteOffset: number, byteLength: number, byteStride?: number, name?: string): IBufferView;
         /**
          * Creates an accessor based on the supplied arguments
          * @param bufferviewIndex The index of the bufferview referenced by this accessor
@@ -832,7 +832,7 @@ declare module BABYLON.GLTF2 {
          * @param max Maximum value of each component in this attribute
          * @returns accessor for glTF
          */
-        static CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
+        static _CreateAccessor(bufferviewIndex: number, name: string, type: AccessorType, componentType: AccessorComponentType, count: number, byteOffset: Nullable<number>, min: Nullable<number[]>, max: Nullable<number[]>): IAccessor;
         /**
          * Calculates the minimum and maximum values of an array of position floats
          * @param positions Positions array of a mesh
@@ -840,7 +840,7 @@ declare module BABYLON.GLTF2 {
          * @param vertexCount Number of vertices to check for min and max values
          * @returns min number array and max number array
          */
-        static CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
+        static _CalculateMinMaxPositions(positions: FloatArray, vertexStart: number, vertexCount: number, convertToRightHandedSystem: boolean): {
             min: number[];
             max: number[];
         };
@@ -849,52 +849,53 @@ declare module BABYLON.GLTF2 {
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedPositionVector3(vector: Vector3): Vector3;
+        static _GetRightHandedPositionVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedPositionVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedPositionVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedPositionArray3FromRef(vector: number[]): void;
+        static _GetRightHandedPositionArray3FromRef(vector: number[]): void;
         /**
          * Converts a new right-handed Vector3
          * @param vector vector3 array
          * @returns right-handed Vector3
          */
-        static GetRightHandedNormalVector3(vector: Vector3): Vector3;
+        static _GetRightHandedNormalVector3(vector: Vector3): Vector3;
         /**
          * Converts a Vector3 to right-handed
          * @param vector Vector3 to convert to right-handed
          */
-        static GetRightHandedNormalVector3FromRef(vector: Vector3): void;
+        static _GetRightHandedNormalVector3FromRef(vector: Vector3): void;
         /**
          * Converts a three element number array to right-handed
          * @param vector number array to convert to right-handed
          */
-        static GetRightHandedNormalArray3FromRef(vector: number[]): void;
+        static _GetRightHandedNormalArray3FromRef(vector: number[]): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedVector4FromRef(vector: Vector4): void;
+        static _GetRightHandedVector4FromRef(vector: Vector4): void;
         /**
          * Converts a Vector4 to right-handed
          * @param vector Vector4 to convert to right-handed
          */
-        static GetRightHandedArray4FromRef(vector: number[]): void;
+        static _GetRightHandedArray4FromRef(vector: number[]): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
+        static _GetRightHandedQuaternionFromRef(quaternion: Quaternion): void;
         /**
          * Converts a Quaternion to right-handed
          * @param quaternion Source quaternion to convert to right-handed
          */
-        static GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _GetRightHandedQuaternionArrayFromRef(quaternion: number[]): void;
+        static _NormalizeTangentFromRef(tangent: Vector4): void;
     }
 }

+ 2 - 58
dist/preview release/typedocValidationBaseline.json

@@ -1,7 +1,7 @@
 {
-  "errors": 4295,
+  "errors": 4283,
   "babylon.typedoc.json": {
-    "errors": 4295,
+    "errors": 4283,
     "AbstractScene": {
       "Property": {
         "effectLayers": {
@@ -6303,9 +6303,6 @@
     "GPUParticleSystem": {
       "Method": {
         "addColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          },
           "Parameter": {
             "color1": {
               "Comments": {
@@ -6313,21 +6310,6 @@
               }
             }
           }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
         }
       }
     },
@@ -10792,31 +10774,11 @@
             "MissingReturn": true
           }
         },
-        "addLifeTimeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "removeColorGradient": {
           "Comments": {
             "MissingReturn": true
           }
         },
-        "removeLifeTimeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
         "_GetEffectCreationOptions": {
           "Comments": {
             "MissingText": true
@@ -22642,9 +22604,6 @@
     "IParticleSystem": {
       "Method": {
         "addColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          },
           "Parameter": {
             "color1": {
               "Comments": {
@@ -22652,21 +22611,6 @@
               }
             }
           }
-        },
-        "addSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeColorGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
-        },
-        "removeSizeGradient": {
-          "Comments": {
-            "MissingReturn": true
-          }
         }
       }
     },

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

@@ -1510,6 +1510,10 @@ declare module BabylonViewer {
                 */
             onViewerInitDoneObservable: BABYLON.Observable<any>;
             /**
+                * Will notify when the viewer init started (after configuration was loaded)
+                */
+            onViewerInitStartedObservable: BABYLON.Observable<any>;
+            /**
                 * Functions added to this observable will be executed on each frame rendered.
                 */
             onFrameRenderedObservable: BABYLON.Observable<any>;

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


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


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

@@ -1510,6 +1510,10 @@ declare module 'babylonjs-viewer/managers/observablesManager' {
                 */
             onViewerInitDoneObservable: Observable<any>;
             /**
+                * Will notify when the viewer init started (after configuration was loaded)
+                */
+            onViewerInitStartedObservable: Observable<any>;
+            /**
                 * Functions added to this observable will be executed on each frame rendered.
                 */
             onFrameRenderedObservable: Observable<any>;

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

@@ -23,6 +23,7 @@
   - Added support for `minInitialRotation` and `maxInitialRotation`. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
   - Added support for size gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#size)
   - Added support for life time gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#lifetime)
+  - Added support for angular speed gradients. [Doc](https://doc.babylonjs.com/babylon101/particles#rotation)
 - Added SceneComponent to help decoupling Scene from its components ([sebavan](http://www.github.com/sebavan))
 
 ## Updates
@@ -62,6 +63,7 @@
 - Added internal texture `format` support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 - Added canvas toBlob polyfill in tools ([sebavan](http://www.github.com/sebavan))
 - Added `RawCubeTexture` class with RGBD and mipmap support ([bghgary](http://www.github.com/bghgary))
+- Added effect layer per rendering group addressing [Issue 4463](https://github.com/BabylonJS/Babylon.js/issues/4463) ([sebavan](http://www.github.com/sebavan))
 
 ### glTF Loader
 
@@ -84,6 +86,7 @@
 - An initial support for WebVR is implemented ([RaananW](https://github.com/RaananW))
 - It is now possible to choose the element that goes fullscreen in the default viewer ([RaananW](https://github.com/RaananW))
 - The default viewer has a plugin system with which new buttons can be added externally ([RaananW](https://github.com/RaananW))
+- The extended configuration is now the default when not providing the "extended" parameter ([RaananW](https://github.com/RaananW))
 
 ### Documentation
 

+ 1 - 1
src/Culling/babylon.boundingSphere.ts

@@ -59,7 +59,7 @@
 
             var distance = Math.sqrt((x * x) + (y * y) + (z * z));
 
-            if (Math.abs(this.radiusWorld - distance) < Epsilon)
+            if (this.radiusWorld < distance)
                 return false;
 
             return true;

+ 11 - 8
src/Gizmos/babylon.axisDragGizmo.ts

@@ -3,7 +3,10 @@ module BABYLON {
      * Single axis drag gizmo
      */
     export class AxisDragGizmo extends Gizmo {
-        private _dragBehavior:PointerDragBehavior;
+        /**
+         * Drag behavior responsible for the gizmos dragging interactions
+         */
+        public dragBehavior:PointerDragBehavior;
         private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
         /**
          * Drag distance in babylon units that the gizmo will snap to when dragged (Default: 0)
@@ -55,10 +58,10 @@ module BABYLON {
             var tmpVector = new Vector3();
             var tmpSnapEvent = {snapDistance: 0};
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
-            this._dragBehavior.moveAttached = false;
-            this._rootMesh.addBehavior(this._dragBehavior);
-            this._dragBehavior.onDragObservable.add((event)=>{
+            this.dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
+            this.dragBehavior.moveAttached = false;
+            this._rootMesh.addBehavior(this.dragBehavior);
+            this.dragBehavior.onDragObservable.add((event)=>{
                 if(this.attachedMesh){
                     // Snapping logic
                     if(this.snapDistance == 0){
@@ -91,8 +94,8 @@ module BABYLON {
             });
         }
         protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
-            if(this._dragBehavior){
-                this._dragBehavior.enabled = value?true:false;
+            if(this.dragBehavior){
+                this.dragBehavior.enabled = value?true:false;
             }
         }
         /**
@@ -101,7 +104,7 @@ module BABYLON {
         public dispose(){
             this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
-            this._dragBehavior.detach();
+            this.dragBehavior.detach();
             super.dispose();
         } 
     }

+ 11 - 8
src/Gizmos/babylon.axisScaleGizmo.ts

@@ -3,7 +3,10 @@ module BABYLON {
      * Single axis scale gizmo
      */
     export class AxisScaleGizmo extends Gizmo {
-        private _dragBehavior:PointerDragBehavior;
+        /**
+         * Drag behavior responsible for the gizmos dragging interactions
+         */
+        public dragBehavior:PointerDragBehavior;
         private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
         /**
          * Scale distance in babylon units that the gizmo will snap to when dragged (Default: 0)
@@ -51,14 +54,14 @@ module BABYLON {
             this._rootMesh.addChild(arrow);
 
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
-            this._dragBehavior.moveAttached = false;
-            this._rootMesh.addBehavior(this._dragBehavior);
+            this.dragBehavior = new PointerDragBehavior({dragAxis: dragAxis});
+            this.dragBehavior.moveAttached = false;
+            this._rootMesh.addBehavior(this.dragBehavior);
 
             var currentSnapDragDistance = 0;
             var tmpVector = new Vector3();
             var tmpSnapEvent = {snapDistance: 0};
-            this._dragBehavior.onDragObservable.add((event)=>{                
+            this.dragBehavior.onDragObservable.add((event)=>{                
                 if(this.attachedMesh){
                     // Snapping logic
                     var snapped = false;
@@ -114,8 +117,8 @@ module BABYLON {
         }
         
         protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
-            if(this._dragBehavior){
-                this._dragBehavior.enabled = value ? true : false;
+            if(this.dragBehavior){
+                this.dragBehavior.enabled = value ? true : false;
             }
         }
         
@@ -125,7 +128,7 @@ module BABYLON {
         public dispose(){
             this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
-            this._dragBehavior.detach();
+            this.dragBehavior.detach();
             super.dispose();
         } 
     }

+ 7 - 4
src/Gizmos/babylon.gizmo.ts

@@ -8,6 +8,7 @@ module BABYLON {
          */
         protected _rootMesh:Mesh;
         private _attachedMesh:Nullable<AbstractMesh>;
+        private _scaleFactor = 3;
         /**
          * 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
@@ -44,11 +45,8 @@ module BABYLON {
          */
         constructor(/** The utility layer the gizmo will be added to */ public gizmoLayer:UtilityLayerRenderer=UtilityLayerRenderer.DefaultUtilityLayer){
             this._rootMesh = new BABYLON.Mesh("gizmoRootNode",gizmoLayer.utilityLayerScene);
+            var tempVector = new Vector3();
             this._beforeRenderObserver = this.gizmoLayer.utilityLayerScene.onBeforeRenderObservable.add(()=>{
-                if(this._updateScale && this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh){
-                    var dist = this.attachedMesh.position.subtract(this.gizmoLayer.utilityLayerScene.activeCamera.position).length()/3;
-                    this._rootMesh.scaling.set(dist, dist, dist);
-                }
                 if(this.attachedMesh){
                     if(this.updateGizmoRotationToMatchAttachedMesh){
                         if(!this._rootMesh.rotationQuaternion){
@@ -59,6 +57,11 @@ module BABYLON {
                     if(this.updateGizmoPositionToMatchAttachedMesh){
                         this._rootMesh.position.copyFrom(this.attachedMesh.absolutePosition);
                     }
+                    if(this._updateScale && this.gizmoLayer.utilityLayerScene.activeCamera && this.attachedMesh){
+                        this._rootMesh.position.subtractToRef(this.gizmoLayer.utilityLayerScene.activeCamera.position, tempVector);
+                        var dist = tempVector.length()/this._scaleFactor;
+                        this._rootMesh.scaling.set(dist, dist, dist);
+                    }
                 }
             })
             this.attachedMesh = null;

+ 12 - 9
src/Gizmos/babylon.planeRotationGizmo.ts

@@ -3,7 +3,10 @@ module BABYLON {
      * Single plane rotation gizmo
      */
     export class PlaneRotationGizmo extends Gizmo {
-        private _dragBehavior:PointerDragBehavior;
+        /**
+         * Drag behavior responsible for the gizmos dragging interactions
+         */
+        public dragBehavior:PointerDragBehavior;
         private _pointerObserver:Nullable<Observer<PointerInfo>> = null;
         
         /**
@@ -47,13 +50,13 @@ module BABYLON {
             
             this._rootMesh.addChild(parentMesh);
             // Add drag behavior to handle events when the gizmo is dragged
-            this._dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal});
-            this._dragBehavior.moveAttached = false;
-            this._rootMesh.addBehavior(this._dragBehavior);
+            this.dragBehavior = new PointerDragBehavior({dragPlaneNormal: planeNormal});
+            this.dragBehavior.moveAttached = false;
+            this._rootMesh.addBehavior(this.dragBehavior);
 
             var lastDragPosition:Nullable<Vector3> = null;
 
-            this._dragBehavior.onDragStartObservable.add((e)=>{
+            this.dragBehavior.onDragStartObservable.add((e)=>{
                 if(this.attachedMesh){
                     lastDragPosition = e.dragPlanePoint;
                 }
@@ -65,7 +68,7 @@ module BABYLON {
 
             var tmpSnapEvent = {snapDistance: 0};
             var currentSnapDragDistance = 0;
-            this._dragBehavior.onDragObservable.add((event)=>{
+            this.dragBehavior.onDragObservable.add((event)=>{
                 if(this.attachedMesh && lastDragPosition){
                     if(!this.attachedMesh.rotationQuaternion){
                         this.attachedMesh.rotationQuaternion = new BABYLON.Quaternion();
@@ -135,8 +138,8 @@ module BABYLON {
         }
 
         protected _attachedMeshChanged(value:Nullable<AbstractMesh>){
-            if(this._dragBehavior){
-                this._dragBehavior.enabled = value ? true : false;
+            if(this.dragBehavior){
+                this.dragBehavior.enabled = value ? true : false;
             }
         }
 
@@ -146,7 +149,7 @@ module BABYLON {
         public dispose(){
             this.onSnapObservable.clear();
             this.gizmoLayer.utilityLayerScene.onPointerObservable.remove(this._pointerObserver);
-            this._dragBehavior.detach();
+            this.dragBehavior.detach();
             super.dispose();
         } 
     }

+ 27 - 18
src/Gizmos/babylon.positionGizmo.ts

@@ -3,15 +3,24 @@ module BABYLON {
      * Gizmo that enables dragging a mesh along 3 axis
      */
     export class PositionGizmo extends Gizmo {
-        private _xDrag:AxisDragGizmo;
-        private _yDrag:AxisDragGizmo;
-        private _zDrag:AxisDragGizmo;
+        /**
+         * Internal gizmo used for interactions on the x axis
+         */
+        public xGizmo:AxisDragGizmo;
+        /**
+         * Internal gizmo used for interactions on the y axis
+         */
+        public yGizmo:AxisDragGizmo;
+        /**
+         * Internal gizmo used for interactions on the z axis
+         */
+        public zGizmo:AxisDragGizmo;
 
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
-            if(this._xDrag){
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+            if(this.xGizmo){
+                this.xGizmo.attachedMesh = mesh;
+                this.yGizmo.attachedMesh = mesh;
+                this.zGizmo.attachedMesh = mesh;
             }
         }
     /**
@@ -20,30 +29,30 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer=UtilityLayerRenderer.DefaultUtilityLayer){
             super(gizmoLayer);
-            this._xDrag = new AxisDragGizmo(new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5), gizmoLayer);
-            this._yDrag = new AxisDragGizmo(new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5), gizmoLayer);
-            this._zDrag = new AxisDragGizmo(new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5), gizmoLayer);
+            this.xGizmo = new AxisDragGizmo(new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5), gizmoLayer);
+            this.yGizmo = new AxisDragGizmo(new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5), gizmoLayer);
+            this.zGizmo = new AxisDragGizmo(new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5), gizmoLayer);
             this.attachedMesh = null;
         }
 
         public set updateGizmoRotationToMatchAttachedMesh(value:boolean){
-            if(this._xDrag){
-                this._xDrag.updateGizmoRotationToMatchAttachedMesh = value;
-                this._yDrag.updateGizmoRotationToMatchAttachedMesh = value;
-                this._zDrag.updateGizmoRotationToMatchAttachedMesh = value;
+            if(this.xGizmo){
+                this.xGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                this.yGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                this.zGizmo.updateGizmoRotationToMatchAttachedMesh = value;
             }
         }
         public get updateGizmoRotationToMatchAttachedMesh(){
-            return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
+            return this.xGizmo.updateGizmoRotationToMatchAttachedMesh;
         }
         
         /**
          * Disposes of the gizmo
          */
         public dispose(){
-            this._xDrag.dispose();
-            this._yDrag.dispose();
-            this._zDrag.dispose();
+            this.xGizmo.dispose();
+            this.yGizmo.dispose();
+            this.zGizmo.dispose();
         }
     }
 }

+ 27 - 18
src/Gizmos/babylon.rotationGizmo.ts

@@ -3,15 +3,24 @@ module BABYLON {
      * Gizmo that enables rotating a mesh along 3 axis
      */
     export class RotationGizmo extends Gizmo {
-        private _xDrag:PlaneRotationGizmo;
-        private _yDrag:PlaneRotationGizmo;
-        private _zDrag:PlaneRotationGizmo;
+        /**
+         * Internal gizmo used for interactions on the x axis
+         */
+        public xGizmo:PlaneRotationGizmo;
+        /**
+         * Internal gizmo used for interactions on the y axis
+         */
+        public yGizmo:PlaneRotationGizmo;
+        /**
+         * Internal gizmo used for interactions on the z axis
+         */
+        public zGizmo:PlaneRotationGizmo;
 
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
-            if(this._xDrag){
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+            if(this.xGizmo){
+                this.xGizmo.attachedMesh = mesh;
+                this.yGizmo.attachedMesh = mesh;
+                this.zGizmo.attachedMesh = mesh;
             }
         }
         /**
@@ -20,30 +29,30 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer=UtilityLayerRenderer.DefaultUtilityLayer){
             super(gizmoLayer);
-            this._xDrag = new PlaneRotationGizmo(new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5), gizmoLayer);
-            this._yDrag = new PlaneRotationGizmo(new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5), gizmoLayer);
-            this._zDrag = new PlaneRotationGizmo(new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5), gizmoLayer);
+            this.xGizmo = new PlaneRotationGizmo(new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5), gizmoLayer);
+            this.yGizmo = new PlaneRotationGizmo(new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5), gizmoLayer);
+            this.zGizmo = new PlaneRotationGizmo(new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5), gizmoLayer);
             this.attachedMesh = null;
         }
 
         public set updateGizmoRotationToMatchAttachedMesh(value:boolean){
-            if(this._xDrag){
-                this._xDrag.updateGizmoRotationToMatchAttachedMesh = value;
-                this._yDrag.updateGizmoRotationToMatchAttachedMesh = value;
-                this._zDrag.updateGizmoRotationToMatchAttachedMesh = value;
+            if(this.xGizmo){
+                this.xGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                this.yGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                this.zGizmo.updateGizmoRotationToMatchAttachedMesh = value;
             }
         }
         public get updateGizmoRotationToMatchAttachedMesh(){
-            return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
+            return this.xGizmo.updateGizmoRotationToMatchAttachedMesh;
         }
 
         /**
          * Disposes of the gizmo
          */
         public dispose(){
-            this._xDrag.dispose();
-            this._yDrag.dispose();
-            this._zDrag.dispose();
+            this.xGizmo.dispose();
+            this.yGizmo.dispose();
+            this.zGizmo.dispose();
         }
     }
 }

+ 27 - 18
src/Gizmos/babylon.scaleGizmo.ts

@@ -3,15 +3,24 @@ module BABYLON {
      * Gizmo that enables scaling a mesh along 3 axis
      */
     export class ScaleGizmo extends Gizmo {
-        private _xDrag:AxisScaleGizmo;
-        private _yDrag:AxisScaleGizmo;
-        private _zDrag:AxisScaleGizmo;
+        /**
+         * Internal gizmo used for interactions on the x axis
+         */
+        public xGizmo:AxisScaleGizmo;
+        /**
+         * Internal gizmo used for interactions on the y axis
+         */
+        public yGizmo:AxisScaleGizmo;
+        /**
+         * Internal gizmo used for interactions on the z axis
+         */
+        public zGizmo:AxisScaleGizmo;
 
         public set attachedMesh(mesh:Nullable<AbstractMesh>){
-            if(this._xDrag){
-                this._xDrag.attachedMesh = mesh;
-                this._yDrag.attachedMesh = mesh;
-                this._zDrag.attachedMesh = mesh;
+            if(this.xGizmo){
+                this.xGizmo.attachedMesh = mesh;
+                this.yGizmo.attachedMesh = mesh;
+                this.zGizmo.attachedMesh = mesh;
             }
         }
         /**
@@ -20,30 +29,30 @@ module BABYLON {
          */
         constructor(gizmoLayer:UtilityLayerRenderer=UtilityLayerRenderer.DefaultUtilityLayer){
             super(gizmoLayer);
-            this._xDrag = new AxisScaleGizmo(new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5), gizmoLayer);
-            this._yDrag = new AxisScaleGizmo(new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5), gizmoLayer);
-            this._zDrag = new AxisScaleGizmo(new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5), gizmoLayer);
+            this.xGizmo = new AxisScaleGizmo(new Vector3(1,0,0), BABYLON.Color3.Green().scale(0.5), gizmoLayer);
+            this.yGizmo = new AxisScaleGizmo(new Vector3(0,1,0), BABYLON.Color3.Red().scale(0.5), gizmoLayer);
+            this.zGizmo = new AxisScaleGizmo(new Vector3(0,0,1), BABYLON.Color3.Blue().scale(0.5), gizmoLayer);
             this.attachedMesh = null;
         }
 
         public set updateGizmoRotationToMatchAttachedMesh(value:boolean){
-            if(this._xDrag){
-                this._xDrag.updateGizmoRotationToMatchAttachedMesh = value;
-                this._yDrag.updateGizmoRotationToMatchAttachedMesh = value;
-                this._zDrag.updateGizmoRotationToMatchAttachedMesh = value;
+            if(this.xGizmo){
+                this.xGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                this.yGizmo.updateGizmoRotationToMatchAttachedMesh = value;
+                this.zGizmo.updateGizmoRotationToMatchAttachedMesh = value;
             }
         }
         public get updateGizmoRotationToMatchAttachedMesh(){
-            return this._xDrag.updateGizmoRotationToMatchAttachedMesh;
+            return this.xGizmo.updateGizmoRotationToMatchAttachedMesh;
         }
 
         /**
          * Disposes of the gizmo
          */
         public dispose(){
-            this._xDrag.dispose();
-            this._yDrag.dispose();
-            this._zDrag.dispose();
+            this.xGizmo.dispose();
+            this.yGizmo.dispose();
+            this.zGizmo.dispose();
         }
     }
 }

+ 19 - 2
src/Layer/babylon.effectLayer.ts

@@ -24,6 +24,11 @@
          * The camera attached to the layer.
          */
         camera: Nullable<Camera>;
+
+        /**
+         * The rendering group to draw the layer in.
+         */
+        renderingGroupId: number;
     }
 
     /**
@@ -80,6 +85,14 @@
         }
 
         /**
+         * Gets the rendering group id the layer should render in.
+         */
+        @serialize()
+        public get renderingGroupId(): number {
+            return this._effectLayerOptions.renderingGroupId;
+        }
+
+        /**
          * An event triggered when the effect layer has been disposed.
          */
         public onDisposeObservable = new Observable<EffectLayer>();
@@ -197,6 +210,7 @@
                 mainTextureRatio: 0.5,
                 alphaBlendingMode: Engine.ALPHA_COMBINE,
                 camera: null,
+                renderingGroupId: -1,
                 ...options,
             };
 
@@ -489,7 +503,10 @@
          * @returns true if the mesh will be used
          */
         public hasMesh(mesh: AbstractMesh): boolean {
-            return true;
+            if (this.renderingGroupId === -1 || mesh.renderingGroupId === this.renderingGroupId) {
+                return true;
+            }
+            return false;
         }
 
         /**
@@ -692,7 +709,7 @@
          */
         public static Parse(parsedEffectLayer: any, scene: Scene, rootUrl: string): EffectLayer {
             var effectLayerType = Tools.Instantiate(parsedEffectLayer.customType);
-            
+
             return effectLayerType.Parse(parsedEffectLayer, scene, rootUrl);
         }
     }

+ 21 - 4
src/Layer/babylon.effectLayerSceneComponent.ts

@@ -85,8 +85,11 @@
             this.scene._cameraDrawRenderTargetStage.registerStep(SceneComponentConstants.STEP_CAMERADRAWRENDERTARGET_EFFECTLAYER, this, this._renderMainTexture);
 
             this.scene._beforeCameraDrawStage.registerStep(SceneComponentConstants.STEP_BEFORECAMERADRAW_EFFECTLAYER, this, this._setStencil);
+
+            this.scene._afterRenderingGroupDrawStage.registerStep(SceneComponentConstants.STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW, this, this._drawRenderingGroup);
+
             this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER, this, this._setStencilBack);
-            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW, this, this._draw);
+            this.scene._afterCameraDrawStage.registerStep(SceneComponentConstants.STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW, this, this._drawCamera);
         }
 
         /**
@@ -205,16 +208,30 @@
             }
         }
 
-        private _draw(camera: Camera): void {
+        private _draw(renderingGroupId: number): void {
             if (this._renderEffects) {
                 this._engine.setDepthBuffer(false);
                 for (let i = 0; i < this._effectLayers.length; i++) {
-                    if (this._effectLayers[i].shouldRender()) {
-                        this._effectLayers[i].render();
+                    const effectLayer = this._effectLayers[i];
+                    if (effectLayer.renderingGroupId === renderingGroupId) {
+                        if (effectLayer.shouldRender()) {
+                            effectLayer.render();
+                        }
                     }
                 }
                 this._engine.setDepthBuffer(true);
             }
         }
+
+        private _drawCamera(camera: Camera): void {
+            if (this._renderEffects) {
+                this._draw(-1);
+            }
+        }
+        private _drawRenderingGroup(index: number): void {
+            if (!this.scene._isInIntermediateRendering() && this._renderEffects) {
+                this._draw(index);
+            }
+        }
     }
 } 

+ 12 - 1
src/Layer/babylon.glowLayer.ts

@@ -49,6 +49,11 @@
          * Enable MSAA by chosing the number of samples.
          */
         mainTextureSamples?: number;
+
+        /**
+         * The rendering group to draw the layer in.
+         */
+        renderingGroupId: number;
     }
 
     /**
@@ -150,6 +155,7 @@
                 mainTextureFixedSize: undefined,
                 camera: null,
                 mainTextureSamples: 1,
+                renderingGroupId: -1,
                 ...options,
             };
 
@@ -158,7 +164,8 @@
                 alphaBlendingMode: Engine.ALPHA_ADD,
                 camera: this._options.camera,
                 mainTextureFixedSize: this._options.mainTextureFixedSize,
-                mainTextureRatio: this._options.mainTextureRatio
+                mainTextureRatio: this._options.mainTextureRatio,
+                renderingGroupId: this._options.renderingGroupId
             });
         }
 
@@ -443,6 +450,10 @@
          * @returns true if the mesh will be highlighted by the current glow layer
          */
         public hasMesh(mesh: AbstractMesh): boolean {
+            if (!super.hasMesh(mesh)) {
+                return false;
+            }
+
             // Included Mesh
             if (this._includedOnlyMeshes.length) {
                 return this._includedOnlyMeshes.indexOf(mesh.uniqueId) !== -1;

+ 16 - 1
src/Layer/babylon.highlightLayer.ts

@@ -80,6 +80,11 @@
          * Should we display highlight as a solid stroke?
          */
         isStroke?: boolean;
+
+        /**
+         * The rendering group to draw the layer in.
+         */
+        renderingGroupId: number;
     }
 
     /**
@@ -244,6 +249,7 @@
                 blurVerticalSize: 1.0,
                 alphaBlendingMode: Engine.ALPHA_COMBINE,
                 camera: null,
+                renderingGroupId: -1,
                 ...options,
             };
 
@@ -252,7 +258,8 @@
                 alphaBlendingMode: this._options.alphaBlendingMode,
                 camera: this._options.camera,
                 mainTextureFixedSize: this._options.mainTextureFixedSize,
-                mainTextureRatio: this._options.mainTextureRatio
+                mainTextureRatio: this._options.mainTextureRatio,
+                renderingGroupId: this._options.renderingGroupId
             });
 
             // Do not render as long as no meshes have been added
@@ -479,6 +486,10 @@
                 return false;
             };
 
+            if (!super.hasMesh(mesh)) {
+                return false;
+            }
+
             return true;
         }
 
@@ -572,6 +583,10 @@
                 return false;
             }
 
+            if (!super.hasMesh(mesh)) {
+                return false;
+            }
+
             return this._meshes[mesh.uniqueId] !== undefined && this._meshes[mesh.uniqueId] !== null;
         }
 

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

@@ -255,11 +255,13 @@ module BABYLON {
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         * @returns the current particle system
          */
         addColorGradient(gradient: number, color1: Color4, color2?: Color4): IParticleSystem;   
         /**
          * Remove a specific color gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         removeColorGradient(gradient: number): IParticleSystem;
         /**
@@ -267,11 +269,13 @@ module BABYLON {
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         addSizeGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         removeSizeGradient(gradient: number): IParticleSystem;
         /**
@@ -286,5 +290,25 @@ module BABYLON {
          * @returns the list of size gradients
          */
         getSizeGradients(): Nullable<Array<FactorGradient>>;
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        getAngularSpeedGradients(): Nullable<Array<FactorGradient>>;   
+        /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): IParticleSystem;
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        removeAngularSpeedGradient(gradient: number): IParticleSystem;        
     }  
 }

+ 158 - 71
src/Particles/babylon.gpuParticleSystem.ts

@@ -63,7 +63,9 @@
         private _updateEffectOptions: EffectCreationOptions;
 
         private _randomTextureSize: number;
-        private _actualFrame = 0;        
+        private _actualFrame = 0;      
+        
+        private readonly _rawTextureWidth = 256;
 
         /**
          * List of animations used by the particle system.
@@ -441,13 +443,46 @@
          */
         public getSizeGradients(): Nullable<Array<FactorGradient>> {
             return this._sizeGradients;
-        }               
+        }      
+        
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
+            return this._angularSpeedGradients;
+        } 
+
+        private _removeGradient(gradient: number, gradients: Nullable<IValueGradient[]>, texture: RawTexture): GPUParticleSystem {
+            if (!gradients) {
+                return this;
+            }
+
+            let index = 0;
+            for (var valueGradient of gradients) {
+                if (valueGradient.gradient === gradient) {
+                    gradients.splice(index, 1);
+                    break;
+                }
+                index++;
+            }
+
+            if (texture) {
+                texture.dispose();
+            }            
+
+            this._releaseBuffers();
+
+            return this;
+        }    
         
         /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
          * @param color2 defines an additional color used to define a range ([color, color2]) with main color to pick the final color from
+         * @returns the current particle system
          */
         public addColorGradient(gradient: number, color1: Color4, color2?: Color4): GPUParticleSystem {
             if (!this._colorGradients) {
@@ -482,58 +517,52 @@
         /**
          * Remove a specific color gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeColorGradient(gradient: number): GPUParticleSystem {
-            if (!this._colorGradients) {
-                return this;
-            }
-
-            let index = 0;
-            for (var colorGradient of this._colorGradients) {
-                if (colorGradient.gradient === gradient) {
-                    this._colorGradients.splice(index, 1);
-                    break;
-                }
-                index++;
-            }
-
-            if (this._colorGradientsTexture) {
-                this._colorGradientsTexture.dispose();
-                (<any>this._colorGradientsTexture) = null;
-            }            
-
-            this._releaseBuffers();
+            this._removeGradient(gradient, this._colorGradients, this._colorGradientsTexture);
+            (<any>this._colorGradientsTexture) = null;
 
             return this;
         }    
+
+        private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
+        private _angularSpeedGradientsTexture: RawTexture;    
         
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
-        private _sizeGradientsTexture: RawTexture;        
+        private _sizeGradientsTexture: RawTexture;     
+        
+        private _addFactorGradient(factorGradients: FactorGradient[], gradient: number, factor: number) {
+            let valueGradient = new FactorGradient();
+            valueGradient.gradient = gradient;
+            valueGradient.factor1 = factor;
+            factorGradients.push(valueGradient);
+
+            factorGradients.sort((a, b) => {
+                if (a.gradient < b.gradient) {
+                    return -1;
+                } else if (a.gradient > b.gradient) {
+                    return 1;
+                }
+
+                return 0;
+            });
+
+            this._releaseBuffers();               
+        }
         
         /**
          * Adds a new size gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient
+         * @returns the current particle system
          */
         public addSizeGradient(gradient: number, factor: number): GPUParticleSystem {
             if (!this._sizeGradients) {
                 this._sizeGradients = [];
             }
 
-            let sizeGradient = new FactorGradient();
-            sizeGradient.gradient = gradient;
-            sizeGradient.factor1 = factor;
-            this._sizeGradients.push(sizeGradient);
-
-            this._sizeGradients.sort((a, b) => {
-                if (a.gradient < b.gradient) {
-                    return -1;
-                } else if (a.gradient > b.gradient) {
-                    return 1;
-                }
-
-                return 0;
-            });
+            this._addFactorGradient(this._sizeGradients, gradient, factor);
 
             if (this._sizeGradientsTexture) {
                 this._sizeGradientsTexture.dispose();
@@ -548,29 +577,48 @@
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): GPUParticleSystem {
-            if (!this._sizeGradients) {
-                return this;
-            }
+            this._removeGradient(gradient, this._sizeGradients, this._sizeGradientsTexture);
+            (<any>this._sizeGradientsTexture) = null;
 
-            let index = 0;
-            for (var sizeGradient of this._sizeGradients) {
-                if (sizeGradient.gradient === gradient) {
-                    this._sizeGradients.splice(index, 1);
-                    break;
-                }
-                index++;
+            return this;            
+        }   
+        
+        /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient    
+         * @returns the current particle system     
+         */
+        public addAngularSpeedGradient(gradient: number, factor: number): GPUParticleSystem {
+            if (!this._angularSpeedGradients) {
+                this._angularSpeedGradients = [];
             }
 
-            if (this._sizeGradientsTexture) {
-                this._sizeGradientsTexture.dispose();
-                (<any>this._sizeGradientsTexture) = null;
+            this._addFactorGradient(this._angularSpeedGradients, gradient, factor);
+
+            if (this._angularSpeedGradientsTexture) {
+                this._angularSpeedGradientsTexture.dispose();
+                (<any>this._angularSpeedGradientsTexture) = null;
             }
 
-            this._releaseBuffers();               
+            this._releaseBuffers();   
 
             return this;
+        }
+
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAngularSpeedGradient(gradient: number): GPUParticleSystem {
+            this._removeGradient(gradient, this._angularSpeedGradients, this._angularSpeedGradientsTexture);
+            (<any>this._angularSpeedGradientsTexture) = null;
+
+            return this;           
         }            
 
         /**
@@ -618,7 +666,7 @@
                                 "direction1", "direction2", "minEmitBox", "maxEmitBox", "radius", "directionRandomizer", "height", "coneAngle", "stopFactor", 
                                 "angleRange", "radiusRange", "cellInfos"],
                 uniformBuffersNames: [],
-                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler"],
+                samplers:["randomSampler", "randomSampler2", "sizeGradientSampler", "angularSpeedGradientSampler"],
                 defines: "",
                 fallbacks: null,  
                 onCompiled: null,
@@ -679,8 +727,13 @@
                 offset += 3;
             }
 
-            updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
-            offset += 2;
+            if (this._angularSpeedGradientsTexture) {
+                updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1);
+                offset += 1;
+            } else {
+                updateVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2);
+                offset += 2;
+            }
 
             if (this._isAnimationSheetEnabled) {
                 updateVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1);
@@ -713,8 +766,13 @@
                 renderVertexBuffers["initialDirection"] = source.createVertexBuffer("initialDirection", offset, 3, this._attributesStrideSize, true);
                 offset += 3;
             }
-            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 2, this._attributesStrideSize, true);
-            offset += 2;
+
+            renderVertexBuffers["angle"] = source.createVertexBuffer("angle", offset, 1, this._attributesStrideSize, true);
+            if (this._angularSpeedGradientsTexture) {
+                offset++;
+            } else {
+                offset += 2;
+            }
 
             if (this._isAnimationSheetEnabled) {
                 renderVertexBuffers["cellIndex"] = source.createVertexBuffer("cellIndex", offset, 1, this._attributesStrideSize, true);
@@ -746,6 +804,10 @@
                 this._attributesStrideSize -= 4;
             }
 
+            if (this._angularSpeedGradientsTexture) {
+                this._attributesStrideSize -= 1;
+            }            
+
             if (this._isAnimationSheetEnabled) {
                 this._attributesStrideSize += 1;
             }            
@@ -793,7 +855,10 @@
 
                 // angle
                 data.push(0.0);  
-                data.push(0.0); 
+
+                if (!this._angularSpeedGradientsTexture) {
+                    data.push(0.0); 
+                }
 
                 if (this._isAnimationSheetEnabled) {
                     data.push(0.0); 
@@ -842,6 +907,10 @@
             if (this._sizeGradientsTexture) {
                 defines += "\n#define SIZEGRADIENTS";
             }     
+
+            if (this._angularSpeedGradientsTexture) {
+                defines += "\n#define ANGULARSPEEDGRADIENTS";
+            }                 
             
             if (this.isAnimationSheetEnabled) {
                 defines += "\n#define ANIMATESHEET";
@@ -926,36 +995,44 @@
             }             
         }    
 
-        private _createSizeGradientTexture() {
-            if (!this._sizeGradients || !this._sizeGradients.length || this._sizeGradientsTexture) {
+        private _createFactorGradientTexture(factorGradients: Nullable<IValueGradient[]>, textureName: string) {
+            let texture:RawTexture = (<any>this)[textureName];
+
+            if (!factorGradients || !factorGradients.length || texture) {
                 return;
             }
 
-            let textureWidth = 256;
-            let data = new Float32Array(textureWidth);
+            let data = new Float32Array(this._rawTextureWidth);
 
-            for (var x = 0; x < textureWidth; x++) {
-                var ratio = x / textureWidth;
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
 
-                Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
+                Tools.GetCurrentGradient(ratio, factorGradients, (currentGradient, nextGradient, scale) => {
                     data[x] = Scalar.Lerp((<FactorGradient>currentGradient).factor1, (<FactorGradient>nextGradient).factor1, scale);
                 });
             }
 
-            this._sizeGradientsTexture = RawTexture.CreateRTexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
-        }        
+            (<any>this)[textureName] = RawTexture.CreateRTexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+        }            
+
+        private _createSizeGradientTexture() {
+            this._createFactorGradientTexture(this._sizeGradients, "_sizeGradientsTexture");
+        }     
+        
+        private _createAngularSpeedGradientTexture() {
+            this._createFactorGradientTexture(this._angularSpeedGradients, "_angularSpeedGradientsTexture");
+        }     
             
         private _createColorGradientTexture() {
             if (!this._colorGradients || !this._colorGradients.length || this._colorGradientsTexture) {
                 return;
             }
 
-            let textureWidth = 256;
-            let data = new Uint8Array(textureWidth * 4);
+            let data = new Uint8Array(this._rawTextureWidth * 4);
             let tmpColor = Tmp.Color4[0];
 
-            for (var x = 0; x < textureWidth; x++) {
-                var ratio = x / textureWidth;
+            for (var x = 0; x < this._rawTextureWidth; x++) {
+                var ratio = x / this._rawTextureWidth;
 
                 Tools.GetCurrentGradient(ratio, this._colorGradients, (currentGradient, nextGradient, scale) => {
 
@@ -968,7 +1045,7 @@
 
             }
 
-            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, textureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
+            this._colorGradientsTexture = RawTexture.CreateRGBATexture(data, this._rawTextureWidth, 1, this._scene, false, false, Texture.NEAREST_SAMPLINGMODE);
         }
 
         /**
@@ -983,6 +1060,7 @@
 
             this._createColorGradientTexture();
             this._createSizeGradientTexture();
+            this._createAngularSpeedGradientTexture();
 
             this._recreateUpdateEffect();
             this._recreateRenderEffect();
@@ -1046,6 +1124,10 @@
                 this._updateEffect.setTexture("sizeGradientSampler", this._sizeGradientsTexture);      
             }
 
+            if (this._angularSpeedGradientsTexture) {      
+                this._updateEffect.setTexture("angularSpeedGradientSampler", this._angularSpeedGradientsTexture);      
+            }
+
             if (this.particleEmitterType) {
                 this.particleEmitterType.applyToShader(this._updateEffect);
             }
@@ -1205,7 +1287,12 @@
             if (this._sizeGradientsTexture) {
                 this._sizeGradientsTexture.dispose();
                 (<any>this._sizeGradientsTexture) = null;
-            }            
+            }    
+            
+            if (this._angularSpeedGradientsTexture) {
+                this._angularSpeedGradientsTexture.dispose();
+                (<any>this._angularSpeedGradientsTexture) = null;
+            }             
          
             if (this._randomTexture) {
                 this._randomTexture.dispose();

+ 13 - 1
src/Particles/babylon.particle.ts

@@ -79,7 +79,14 @@
         /** @hidden */
         public _currentSize1 = 0;
         /** @hidden */
-        public _currentSize2 = 0;        
+        public _currentSize2 = 0;      
+        
+        /** @hidden */
+        public _currentAngularSpeedGradient: Nullable<FactorGradient>;
+        /** @hidden */
+        public _currentAngularSpeed1 = 0;
+        /** @hidden */
+        public _currentAngularSpeed2 = 0;          
 
         /**
          * Creates a new instance Particle
@@ -147,6 +154,11 @@
                 other._currentSize1 = this._currentSize1;
                 other._currentSize2 = this._currentSize2;
             }
+            if (this._currentAngularSpeedGradient) {
+                other._currentAngularSpeedGradient = this._currentAngularSpeedGradient;
+                other._currentAngularSpeed1 = this._currentAngularSpeed1;
+                other._currentAngularSpeed2 = this._currentAngularSpeed2;
+            }            
             if (this.particleSystem.isAnimationSheetEnabled) {
                 other._initialStartSpriteCellID = this._initialStartSpriteCellID;
                 other._initialEndSpriteCellID = this._initialEndSpriteCellID;

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

@@ -187,6 +187,7 @@
         private _colorGradients: Nullable<Array<ColorGradient>> = null;
         private _sizeGradients: Nullable<Array<FactorGradient>> = null;
         private _lifeTimeGradients: Nullable<Array<FactorGradient>> = null;
+        private _angularSpeedGradients: Nullable<Array<FactorGradient>> = null;
 
         /**
          * Gets the current list of color gradients.
@@ -213,7 +214,16 @@
          */
         public getLifeTimeGradients(): Nullable<Array<FactorGradient>> {
             return this._lifeTimeGradients;
-        }          
+        }   
+        
+        /**
+         * Gets the current list of angular speed gradients.
+         * You must use addAngularSpeedGradient and removeAngularGradient to udpate this list
+         * @returns the list of angular speed gradients
+         */
+        public getAngularSpeedGradients(): Nullable<Array<FactorGradient>> {
+            return this._angularSpeedGradients;
+        } 
 
         /**
          * Random direction of each particle after it has been emitted, between direction1 and direction2 vectors.
@@ -523,6 +533,17 @@
                                 particle.color.a = 0;
                             }
                         }
+
+                        if (this._angularSpeedGradients && this._angularSpeedGradients.length > 0) {                  
+                            Tools.GetCurrentGradient(ratio, this._angularSpeedGradients, (currentGradient, nextGradient, scale) => {
+                                if (currentGradient !== particle._currentAngularSpeedGradient) {
+                                    particle._currentAngularSpeed1 = particle._currentAngularSpeed2;
+                                    particle._currentAngularSpeed2 = (<FactorGradient>nextGradient).getFactor();    
+                                    particle._currentAngularSpeedGradient = (<FactorGradient>currentGradient);
+                                }                                
+                                particle.angularSpeed = Scalar.Lerp(particle._currentAngularSpeed1, particle._currentAngularSpeed2, scale);
+                            });
+                        }                        
                         particle.angle += particle.angularSpeed * this._scaledUpdateSpeed;
 
                         particle.direction.scaleToRef(this._scaledUpdateSpeed, this._scaledDirection);
@@ -531,7 +552,7 @@
                         this.gravity.scaleToRef(this._scaledUpdateSpeed, this._scaledGravity);
                         particle.direction.addInPlace(this._scaledGravity);
 
-                        // Gradient
+                        // Size
                         if (this._sizeGradients && this._sizeGradients.length > 0) {                  
                             Tools.GetCurrentGradient(ratio, this._sizeGradients, (currentGradient, nextGradient, scale) => {
                                 if (currentGradient !== particle._currentSizeGradient) {
@@ -589,6 +610,7 @@
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the life time factor to affect to the specified gradient         
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         public addLifeTimeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
             if (!this._lifeTimeGradients) {
@@ -603,6 +625,7 @@
         /**
          * Remove a specific life time gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeLifeTimeGradient(gradient: number): ParticleSystem {
             this._removeFactorGradient(this._lifeTimeGradients, gradient);
@@ -615,6 +638,7 @@
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param factor defines the size factor to affect to the specified gradient         
          * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
          */
         public addSizeGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
             if (!this._sizeGradients) {
@@ -629,6 +653,7 @@
         /**
          * Remove a specific size gradient
          * @param gradient defines the gradient to remove
+         * @returns the current particle system
          */
         public removeSizeGradient(gradient: number): ParticleSystem {
             this._removeFactorGradient(this._sizeGradients, gradient);
@@ -637,6 +662,34 @@
         }        
 
         /**
+         * Adds a new angular speed gradient
+         * @param gradient defines the gradient to use (between 0 and 1)
+         * @param factor defines the size factor to affect to the specified gradient         
+         * @param factor2 defines an additional factor used to define a range ([factor, factor2]) with main value to pick the final value from
+         * @returns the current particle system
+         */
+        public addAngularSpeedGradient(gradient: number, factor: number, factor2?: number): ParticleSystem {
+            if (!this._angularSpeedGradients) {
+                this._angularSpeedGradients = [];
+            }
+
+            this._addFactorGradient(this._angularSpeedGradients, gradient, factor, factor2);
+
+            return this;
+        }
+
+        /**
+         * Remove a specific angular speed gradient
+         * @param gradient defines the gradient to remove
+         * @returns the current particle system
+         */
+        public removeAngularSpeedGradient(gradient: number): ParticleSystem {
+            this._removeFactorGradient(this._angularSpeedGradients, gradient);
+
+            return this;
+        }           
+
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color defines the color to affect to the specified gradient
@@ -1043,7 +1096,19 @@
                 particle.scale.copyFromFloats(Scalar.RandomRange(this.minScaleX, this.maxScaleX), Scalar.RandomRange(this.minScaleY, this.maxScaleY));
 
                 // Angle
-                particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
+                if (!this._angularSpeedGradients || this._angularSpeedGradients.length === 0) {
+                    particle.angularSpeed = Scalar.RandomRange(this.minAngularSpeed, this.maxAngularSpeed);
+                } else {
+                    particle._currentAngularSpeedGradient = this._angularSpeedGradients[0];
+                    particle.angularSpeed =  particle._currentAngularSpeedGradient.getFactor();
+                    particle._currentAngularSpeed1 = particle.angularSpeed;
+
+                    if (this._angularSpeedGradients.length > 1) {
+                        particle._currentAngularSpeed2 = this._angularSpeedGradients[1].getFactor();
+                    } else {
+                        particle._currentAngularSpeed2 = particle._currentAngularSpeed1;
+                    }
+                }
                 particle.angle = Scalar.RandomRange(this.minInitialRotation, this.maxInitialRotation);
 
                 // Color
@@ -1580,8 +1645,25 @@
 
                     serializationObject.sizeGradients.push(serializedGradient);
                 }
-            }            
+            }       
+                        
+            let angularSpeedGradients = particleSystem.getAngularSpeedGradients();
+            if (angularSpeedGradients) {
+                serializationObject.angularSpeedGradients = [];
+                for (var angularSpeedGradient of angularSpeedGradients) {
 
+                    var serializedGradient: any = {
+                        gradient: angularSpeedGradient.gradient,
+                        factor1: angularSpeedGradient.factor1
+                    };
+
+                    if (angularSpeedGradient.factor2 !== undefined) {
+                        serializedGradient.factor2 = angularSpeedGradient.factor2;
+                    }
+
+                    serializationObject.angularSpeedGradients.push(serializedGradient);
+                }
+            }  
         }
 
         /** @hidden */
@@ -1671,6 +1753,12 @@
                     particleSystem.addSizeGradient(sizeGradient.gradient, sizeGradient.factor1 !== undefined ?  sizeGradient.factor1 : sizeGradient.factor, sizeGradient.factor2);
                 }
             }       
+
+            if (parsedParticleSystem.angularSpeedGradients) {
+                for (var angularSpeedGradient of parsedParticleSystem.angularSpeedGradients) {
+                    particleSystem.addAngularSpeedGradient(angularSpeedGradient.gradient, angularSpeedGradient.factor1 !== undefined ?  angularSpeedGradient.factor1 : angularSpeedGradient.factor, angularSpeedGradient.factor2);
+                }
+            }               
             
             // Emitter
             let emitterType: IParticleEmitterType;

+ 8 - 1
src/Rendering/babylon.renderingManager.ts

@@ -110,8 +110,15 @@
                     observable.notifyObservers(info, renderingGroupMask);
                 }
 
-                if (renderingGroup)
+                if (renderingGroup) {
+                    for (let step of this._scene._beforeRenderingGroupDrawStage) {
+                        step.action(index);
+                    }
                     renderingGroup.render(customRenderFunction, renderSprites, renderParticles, activeMeshes);
+                    for (let step of this._scene._afterRenderingGroupDrawStage) {
+                        step.action(index);
+                    }
+                }
 
                 // Fire POSTTRANSPARENT stage
                 if (observable && info) {

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

@@ -13,7 +13,7 @@ in vec3 size;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
-in vec2 angle;
+in float angle;
 #ifdef ANIMATESHEET
 in float cellIndex;
 #endif
@@ -85,8 +85,8 @@ void main() {
 	rotatedCorner.w = 0.;
 
 	#ifdef BILLBOARDY	
-		rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
-		rotatedCorner.z = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+		rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+		rotatedCorner.z = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 		rotatedCorner.y = 0.;
 
 		vec3 yaxis = position - eyePosition;
@@ -96,8 +96,8 @@ void main() {
 		vec4 viewPosition = (view * vec4(worldPos, 1.0)); 
 	#else
 		// Rotate
-		rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
-		rotatedCorner.y = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+		rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
+		rotatedCorner.y = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 		rotatedCorner.z = 0.;
 
 		// Expand position
@@ -107,9 +107,9 @@ void main() {
 #else
   // Rotate
 	vec3 rotatedCorner;
-	rotatedCorner.x = cornerPos.x * cos(angle.x) - cornerPos.y * sin(angle.x);
+	rotatedCorner.x = cornerPos.x * cos(angle) - cornerPos.y * sin(angle);
 	rotatedCorner.y = 0.;
-	rotatedCorner.z = cornerPos.x * sin(angle.x) + cornerPos.y * cos(angle.x);
+	rotatedCorner.z = cornerPos.x * sin(angle) + cornerPos.y * cos(angle);
 
 	vec3 yaxis = normalize(initialDirection);
 	vec3 worldPos = rotate(yaxis, rotatedCorner);

+ 23 - 0
src/Shaders/gpuUpdateParticles.vertex.fx

@@ -57,7 +57,11 @@ in vec3 direction;
 #ifndef BILLBOARD
 in vec3 initialDirection;
 #endif
+#ifdef ANGULARSPEEDGRADIENTS
+in float angle;
+#else
 in vec2 angle;
+#endif
 #ifdef ANIMATESHEET
 in float cellIndex;
 #endif
@@ -75,7 +79,11 @@ out vec3 outDirection;
 #ifndef BILLBOARD
 out vec3 outInitialDirection;
 #endif
+#ifdef ANGULARSPEEDGRADIENTS
+out float outAngle;
+#else
 out vec2 outAngle;
+#endif
 #ifdef ANIMATESHEET
 out float outCellIndex;
 #endif
@@ -84,6 +92,10 @@ out float outCellIndex;
 uniform sampler2D sizeGradientSampler;
 #endif 
 
+#ifdef ANGULARSPEEDGRADIENTS
+uniform sampler2D angularSpeedGradientSampler;
+#endif 
+
 #ifdef ANIMATESHEET
 uniform vec3 cellInfos;
 #endif
@@ -146,8 +158,12 @@ void main() {
 #endif
 
     // Angular speed
+#ifndef ANGULARSPEEDGRADIENTS    
     outAngle.y = angleRange.x + (angleRange.y - angleRange.x) * randoms.a;
     outAngle.x = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
+#else
+    outAngle = angleRange.z + (angleRange.w - angleRange.z) * randoms.r;
+#endif        
 
     // Position / Direction (based on emitter type)
 #ifdef BOXEMITTER
@@ -240,7 +256,14 @@ void main() {
     outInitialDirection = initialDirection;
 #endif
     outDirection = direction + gravity * timeDelta;
+
+#ifdef ANGULARSPEEDGRADIENTS
+    float angularSpeed = texture(angularSpeedGradientSampler, vec2(age / life, 0)).r;
+    outAngle = angle + angularSpeed * timeDelta;
+#else
     outAngle = vec2(angle.x + angle.y * timeDelta, angle.y);
+#endif
+
 #ifdef ANIMATESHEET      
     float dist = cellInfos.y - cellInfos.x;
     float ratio = clamp(mod(outAge * cellInfos.z, life) / life, 0., 1.0);

+ 11 - 1
src/babylon.scene.ts

@@ -1205,7 +1205,15 @@
          */
         public _beforeCameraDrawStage = Stage.Create<CameraStageAction>();
         /**
-         * Defines the actions happening just after the active camera is drawing.
+         * Defines the actions happening just before a rendering group is drawing.
+         */
+        public _beforeRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
+        /**
+         * Defines the actions happening just after a rendering group has been drawn.
+         */
+        public _afterRenderingGroupDrawStage = Stage.Create<RenderingGroupStageAction>();
+        /**
+         * Defines the actions happening just after the active camera has been drawn.
          */
         public _afterCameraDrawStage = Stage.Create<CameraStageAction>();
        
@@ -5002,6 +5010,8 @@
             this._isReadyForMeshStage.clear();
             this._cameraDrawRenderTargetStage.clear();
             this._beforeCameraDrawStage.clear();
+            this._beforeRenderingGroupDrawStage.clear();
+            this._afterRenderingGroupDrawStage.clear();
             this._afterCameraDrawStage.clear();
             for (let component of this._components) {
                 component.dispose();

+ 7 - 0
src/babylon.sceneComponent.ts

@@ -15,6 +15,8 @@
         public static readonly STEP_BEFORECAMERADRAW_EFFECTLAYER = 0;
         public static readonly STEP_BEFORECAMERADRAW_LAYER = 1;
 
+        public static readonly STEP_AFTERRENDERINGGROUPDRAW_EFFECTLAYER_DRAW = 0;
+
         public static readonly STEP_AFTERCAMERADRAW_EFFECTLAYER = 0;
         public static readonly STEP_AFTERCAMERADRAW_LENSFLARESYSTEM = 1;
         public static readonly STEP_AFTERCAMERADRAW_EFFECTLAYER_DRAW = 2;
@@ -85,6 +87,11 @@
     export type CameraStageAction = (camera: Camera) => void;
 
     /** 
+     * Strong typing of a RenderingGroup related stage step action 
+     */
+    export type RenderingGroupStageAction = (renderingGroupId: number) => void;
+
+    /** 
      * Strong typing of a simple stage step action 
      */
     export type SimpleStageAction = () => void;